From f958cfd2ff88ab788f458b6149aa30fb982d7a4a Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 27 Sep 2019 14:52:19 +0200 Subject: [PATCH 01/67] ENABLE_3DCONNEXION_DEVICES - 1st installment of support for 3Dconnexion devices Implemented using hidapi library (https://github.com/libusb/hidapi) and https://github.com/koenieee/CrossplatformSpacemouseDriver/tree/master/SpaceMouseDriver as reference Unsolved issues: - When manipulating the SpaceNavigator wxWidgets generates a mouse wheel event that needs to be filtered out - wxWidgets does not detect devices being connected/disconnected to the pc - Current state forces a continuous rendering - Current state misses dependence on camera zoom - Non intuitive movement limits - Translation and rotation speed factors are hardcoded - Number of device buttons hardcoded --- CMakeLists.txt | 19 + src/hidapi/hidapi.h | 395 +++++++++ src/hidapi/linux/hid.c | 797 ++++++++++++++++++ src/hidapi/mac/hid.c | 1121 ++++++++++++++++++++++++++ src/hidapi/win/hid.c | 956 ++++++++++++++++++++++ src/libslic3r/Technologies.hpp | 9 + src/slic3r/CMakeLists.txt | 4 +- src/slic3r/GUI/GLCanvas3D.cpp | 26 + src/slic3r/GUI/GLCanvas3D.hpp | 3 + src/slic3r/GUI/MainFrame.cpp | 7 + src/slic3r/GUI/Mouse3DController.cpp | 332 ++++++++ src/slic3r/GUI/Mouse3DController.hpp | 96 +++ src/slic3r/GUI/Plater.cpp | 36 + src/slic3r/GUI/Plater.hpp | 5 + 14 files changed, 3805 insertions(+), 1 deletion(-) create mode 100644 src/hidapi/hidapi.h create mode 100644 src/hidapi/linux/hid.c create mode 100644 src/hidapi/mac/hid.c create mode 100644 src/hidapi/win/hid.c create mode 100644 src/slic3r/GUI/Mouse3DController.cpp create mode 100644 src/slic3r/GUI/Mouse3DController.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 8c7fc12df..b9a7cf065 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -365,6 +365,25 @@ include_directories(${GLEW_INCLUDE_DIRS}) add_library(cereal INTERFACE) target_include_directories(cereal INTERFACE include) +# Find the hidapi library +if(WIN32) +add_library(hidapi STATIC + ${LIBDIR}/hidapi/win/hid.c +) +elseif (APPLE) +add_library(hidapi STATIC + ${LIBDIR}/hidapi/mac/hid.c +) +else () +add_library(hidapi STATIC + ${LIBDIR}/hidapi/linux/hid.c +) +endif () +set(HIDAPI_FOUND 1) +set(HIDAPI_INCLUDE_DIRS ${LIBDIR}/hidapi/) +set(HIDAPI_LIBRARIES hidapi) +include_directories(${HIDAPI_INCLUDE_DIRS}) + # l10n set(L10N_DIR "${SLIC3R_RESOURCES_DIR}/localization") add_custom_target(pot diff --git a/src/hidapi/hidapi.h b/src/hidapi/hidapi.h new file mode 100644 index 000000000..1819f8de0 --- /dev/null +++ b/src/hidapi/hidapi.h @@ -0,0 +1,395 @@ +/******************************************************* + HIDAPI - Multi-Platform library for + communication with HID devices. + + Alan Ott + Signal 11 Software + + 8/22/2009 + + Copyright 2009, All Rights Reserved. + + At the discretion of the user of this library, + this software may be licensed under the terms of the + GNU General Public License v3, a BSD-Style license, or the + original HIDAPI license as outlined in the LICENSE.txt, + LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt + files located at the root of the source distribution. + These files may also be found in the public source + code repository located at: + http://github.com/signal11/hidapi . +********************************************************/ + +/** @file + * @defgroup API hidapi API + */ + +#ifndef HIDAPI_H__ +#define HIDAPI_H__ + +#include + +#ifdef _WIN32 + #define HID_API_EXPORT __declspec(dllexport) + #define HID_API_CALL +#else + #define HID_API_EXPORT /**< API export macro */ + #define HID_API_CALL /**< API call macro */ +#endif + +#define HID_API_EXPORT_CALL HID_API_EXPORT HID_API_CALL /**< API export and call macro*/ + +#ifdef __cplusplus +extern "C" { +#endif + struct hid_device_; + typedef struct hid_device_ hid_device; /**< opaque hidapi structure */ + + /** hidapi info structure */ + struct hid_device_info { + /** Platform-specific device path */ + char *path; + /** Device Vendor ID */ + unsigned short vendor_id; + /** Device Product ID */ + unsigned short product_id; + /** Serial Number */ + wchar_t *serial_number; + /** Device Release Number in binary-coded decimal, + also known as Device Version Number */ + unsigned short release_number; + /** Manufacturer String */ + wchar_t *manufacturer_string; + /** Product string */ + wchar_t *product_string; + /** Usage Page for this Device/Interface + (Windows/Mac only). */ + unsigned short usage_page; + /** Usage for this Device/Interface + (Windows/Mac only).*/ + unsigned short usage; + /** The USB interface which this logical device + represents. + + * Valid on both Linux implementations in all cases. + * Valid on the Windows implementation only if the device + contains more than one interface. + * Valid on the Mac implementation if and only if the device + is a USB HID device. */ + int interface_number; + + /** Pointer to the next device */ + struct hid_device_info *next; + }; + + + /** @brief Initialize the HIDAPI library. + + This function initializes the HIDAPI library. Calling it is not + strictly necessary, as it will be called automatically by + hid_enumerate() and any of the hid_open_*() functions if it is + needed. This function should be called at the beginning of + execution however, if there is a chance of HIDAPI handles + being opened by different threads simultaneously. + + @ingroup API + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_init(void); + + /** @brief Finalize the HIDAPI library. + + This function frees all of the static data associated with + HIDAPI. It should be called at the end of execution to avoid + memory leaks. + + @ingroup API + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_exit(void); + + /** @brief Enumerate the HID Devices. + + This function returns a linked list of all the HID devices + attached to the system which match vendor_id and product_id. + If @p vendor_id is set to 0 then any vendor matches. + If @p product_id is set to 0 then any product matches. + If @p vendor_id and @p product_id are both set to 0, then + all HID devices will be returned. + + @ingroup API + @param vendor_id The Vendor ID (VID) of the types of device + to open. + @param product_id The Product ID (PID) of the types of + device to open. + + @returns + This function returns a pointer to a linked list of type + struct #hid_device_info, containing information about the HID devices + attached to the system, or NULL in the case of failure. Free + this linked list by calling hid_free_enumeration(). + */ + struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id); + + /** @brief Free an enumeration Linked List + + This function frees a linked list created by hid_enumerate(). + + @ingroup API + @param devs Pointer to a list of struct_device returned from + hid_enumerate(). + */ + void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs); + + /** @brief Open a HID device using a Vendor ID (VID), Product ID + (PID) and optionally a serial number. + + If @p serial_number is NULL, the first device with the + specified VID and PID is opened. + + @ingroup API + @param vendor_id The Vendor ID (VID) of the device to open. + @param product_id The Product ID (PID) of the device to open. + @param serial_number The Serial Number of the device to open + (Optionally NULL). + + @returns + This function returns a pointer to a #hid_device object on + success or NULL on failure. + */ + HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number); + + /** @brief Open a HID device by its path name. + + The path name be determined by calling hid_enumerate(), or a + platform-specific path name can be used (eg: /dev/hidraw0 on + Linux). + + @ingroup API + @param path The path name of the device to open + + @returns + This function returns a pointer to a #hid_device object on + success or NULL on failure. + */ + HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path); + + /** @brief Write an Output report to a HID device. + + The first byte of @p data[] must contain the Report ID. For + devices which only support a single report, this must be set + to 0x0. The remaining bytes contain the report data. Since + the Report ID is mandatory, calls to hid_write() will always + contain one more byte than the report contains. For example, + if a hid report is 16 bytes long, 17 bytes must be passed to + hid_write(), the Report ID (or 0x0, for devices with a + single report), followed by the report data (16 bytes). In + this example, the length passed in would be 17. + + hid_write() will send the data on the first OUT endpoint, if + one exists. If it does not, it will send the data through + the Control Endpoint (Endpoint 0). + + @ingroup API + @param dev A device handle returned from hid_open(). + @param data The data to send, including the report number as + the first byte. + @param length The length in bytes of the data to send. + + @returns + This function returns the actual number of bytes written and + -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length); + + /** @brief Read an Input report from a HID device with timeout. + + Input reports are returned + to the host through the INTERRUPT IN endpoint. The first byte will + contain the Report number if the device uses numbered reports. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param data A buffer to put the read data into. + @param length The number of bytes to read. For devices with + multiple reports, make sure to read an extra byte for + the report number. + @param milliseconds timeout in milliseconds or -1 for blocking wait. + + @returns + This function returns the actual number of bytes read and + -1 on error. If no packet was available to be read within + the timeout period, this function returns 0. + */ + int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds); + + /** @brief Read an Input report from a HID device. + + Input reports are returned + to the host through the INTERRUPT IN endpoint. The first byte will + contain the Report number if the device uses numbered reports. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param data A buffer to put the read data into. + @param length The number of bytes to read. For devices with + multiple reports, make sure to read an extra byte for + the report number. + + @returns + This function returns the actual number of bytes read and + -1 on error. If no packet was available to be read and + the handle is in non-blocking mode, this function returns 0. + */ + int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length); + + /** @brief Set the device handle to be non-blocking. + + In non-blocking mode calls to hid_read() will return + immediately with a value of 0 if there is no data to be + read. In blocking mode, hid_read() will wait (block) until + there is data to read before returning. + + Nonblocking can be turned on and off at any time. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param nonblock enable or not the nonblocking reads + - 1 to enable nonblocking + - 0 to disable nonblocking. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock); + + /** @brief Send a Feature report to the device. + + Feature reports are sent over the Control endpoint as a + Set_Report transfer. The first byte of @p data[] must + contain the Report ID. For devices which only support a + single report, this must be set to 0x0. The remaining bytes + contain the report data. Since the Report ID is mandatory, + calls to hid_send_feature_report() will always contain one + more byte than the report contains. For example, if a hid + report is 16 bytes long, 17 bytes must be passed to + hid_send_feature_report(): the Report ID (or 0x0, for + devices which do not use numbered reports), followed by the + report data (16 bytes). In this example, the length passed + in would be 17. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param data The data to send, including the report number as + the first byte. + @param length The length in bytes of the data to send, including + the report number. + + @returns + This function returns the actual number of bytes written and + -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length); + + /** @brief Get a feature report from a HID device. + + Set the first byte of @p data[] to the Report ID of the + report to be read. Make sure to allow space for this + extra byte in @p data[]. Upon return, the first byte will + still contain the Report ID, and the report data will + start in data[1]. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param data A buffer to put the read data into, including + the Report ID. Set the first byte of @p data[] to the + Report ID of the report to be read, or set it to zero + if your device does not use numbered reports. + @param length The number of bytes to read, including an + extra byte for the report ID. The buffer can be longer + than the actual report. + + @returns + This function returns the number of bytes read plus + one for the report ID (which is still in the first + byte), or -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length); + + /** @brief Close a HID device. + + @ingroup API + @param dev A device handle returned from hid_open(). + */ + void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev); + + /** @brief Get The Manufacturer String from a HID device. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen); + + /** @brief Get The Product String from a HID device. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen); + + /** @brief Get The Serial Number String from a HID device. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen); + + /** @brief Get a string from a HID device, based on its string index. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param string_index The index of the string to get. + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen); + + /** @brief Get a string describing the last error which occurred. + + @ingroup API + @param dev A device handle returned from hid_open(). + + @returns + This function returns a string containing the last error + which occurred or NULL if none has occurred. + */ + HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *dev); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/hidapi/linux/hid.c b/src/hidapi/linux/hid.c new file mode 100644 index 000000000..56dac0fab --- /dev/null +++ b/src/hidapi/linux/hid.c @@ -0,0 +1,797 @@ +/******************************************************* + HIDAPI - Multi-Platform library for + communication with HID devices. + + Alan Ott + Signal 11 Software + + 8/22/2009 + Linux Version - 6/2/2009 + + Copyright 2009, All Rights Reserved. + + At the discretion of the user of this library, + this software may be licensed under the terms of the + GNU General Public License v3, a BSD-Style license, or the + original HIDAPI license as outlined in the LICENSE.txt, + LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt + files located at the root of the source distribution. + These files may also be found in the public source + code repository located at: + http://github.com/signal11/hidapi . +********************************************************/ + +/* C */ +#include +#include +#include +#include +#include + +/* Unix */ +#include +#include +#include +#include +#include +#include +#include + +/* Linux */ +#include +#include +#include +#include + +#include "hidapi.h" + +/* Definitions from linux/hidraw.h. Since these are new, some distros + may not have header files which contain them. */ +#ifndef HIDIOCSFEATURE +#define HIDIOCSFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x06, len) +#endif +#ifndef HIDIOCGFEATURE +#define HIDIOCGFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x07, len) +#endif + + +/* USB HID device property names */ +const char *device_string_names[] = { + "manufacturer", + "product", + "serial", +}; + +/* Symbolic names for the properties above */ +enum device_string_id { + DEVICE_STRING_MANUFACTURER, + DEVICE_STRING_PRODUCT, + DEVICE_STRING_SERIAL, + + DEVICE_STRING_COUNT, +}; + +struct hid_device_ { + int device_handle; + int blocking; + int uses_numbered_reports; +}; + + +static __u32 kernel_version = 0; + +static __u32 detect_kernel_version(void) +{ + struct utsname name; + int major, minor, release; + int ret; + + uname(&name); + ret = sscanf(name.release, "%d.%d.%d", &major, &minor, &release); + if (ret == 3) { + return KERNEL_VERSION(major, minor, release); + } + + ret = sscanf(name.release, "%d.%d", &major, &minor); + if (ret == 2) { + return KERNEL_VERSION(major, minor, 0); + } + + printf("Couldn't determine kernel version from version string \"%s\"\n", name.release); + return 0; +} + +static hid_device *new_hid_device(void) +{ + hid_device *dev = calloc(1, sizeof(hid_device)); + dev->device_handle = -1; + dev->blocking = 1; + dev->uses_numbered_reports = 0; + + return dev; +} + + +/* The caller must free the returned string with free(). */ +static wchar_t *utf8_to_wchar_t(const char *utf8) +{ + wchar_t *ret = NULL; + + if (utf8) { + size_t wlen = mbstowcs(NULL, utf8, 0); + if ((size_t) -1 == wlen) { + return wcsdup(L""); + } + ret = calloc(wlen+1, sizeof(wchar_t)); + mbstowcs(ret, utf8, wlen+1); + ret[wlen] = 0x0000; + } + + return ret; +} + +/* Get an attribute value from a udev_device and return it as a whar_t + string. The returned string must be freed with free() when done.*/ +static wchar_t *copy_udev_string(struct udev_device *dev, const char *udev_name) +{ + return utf8_to_wchar_t(udev_device_get_sysattr_value(dev, udev_name)); +} + +/* uses_numbered_reports() returns 1 if report_descriptor describes a device + which contains numbered reports. */ +static int uses_numbered_reports(__u8 *report_descriptor, __u32 size) { + unsigned int i = 0; + int size_code; + int data_len, key_size; + + while (i < size) { + int key = report_descriptor[i]; + + /* Check for the Report ID key */ + if (key == 0x85/*Report ID*/) { + /* This device has a Report ID, which means it uses + numbered reports. */ + return 1; + } + + //printf("key: %02hhx\n", key); + + if ((key & 0xf0) == 0xf0) { + /* This is a Long Item. The next byte contains the + length of the data section (value) for this key. + See the HID specification, version 1.11, section + 6.2.2.3, titled "Long Items." */ + if (i+1 < size) + data_len = report_descriptor[i+1]; + else + data_len = 0; /* malformed report */ + key_size = 3; + } + else { + /* This is a Short Item. The bottom two bits of the + key contain the size code for the data section + (value) for this key. Refer to the HID + specification, version 1.11, section 6.2.2.2, + titled "Short Items." */ + size_code = key & 0x3; + switch (size_code) { + case 0: + case 1: + case 2: + data_len = size_code; + break; + case 3: + data_len = 4; + break; + default: + /* Can't ever happen since size_code is & 0x3 */ + data_len = 0; + break; + }; + key_size = 1; + } + + /* Skip over this key and it's associated data */ + i += data_len + key_size; + } + + /* Didn't find a Report ID key. Device doesn't use numbered reports. */ + return 0; +} + +/* + * The caller is responsible for free()ing the (newly-allocated) character + * strings pointed to by serial_number_utf8 and product_name_utf8 after use. + */ +static int +parse_uevent_info(const char *uevent, int *bus_type, + unsigned short *vendor_id, unsigned short *product_id, + char **serial_number_utf8, char **product_name_utf8) +{ + char *tmp = strdup(uevent); + char *saveptr = NULL; + char *line; + char *key; + char *value; + + int found_id = 0; + int found_serial = 0; + int found_name = 0; + + line = strtok_r(tmp, "\n", &saveptr); + while (line != NULL) { + /* line: "KEY=value" */ + key = line; + value = strchr(line, '='); + if (!value) { + goto next_line; + } + *value = '\0'; + value++; + + if (strcmp(key, "HID_ID") == 0) { + /** + * type vendor product + * HID_ID=0003:000005AC:00008242 + **/ + int ret = sscanf(value, "%x:%hx:%hx", bus_type, vendor_id, product_id); + if (ret == 3) { + found_id = 1; + } + } else if (strcmp(key, "HID_NAME") == 0) { + /* The caller has to free the product name */ + *product_name_utf8 = strdup(value); + found_name = 1; + } else if (strcmp(key, "HID_UNIQ") == 0) { + /* The caller has to free the serial number */ + *serial_number_utf8 = strdup(value); + found_serial = 1; + } + +next_line: + line = strtok_r(NULL, "\n", &saveptr); + } + + free(tmp); + return (found_id && found_name && found_serial); +} + + +static int get_device_string(hid_device *dev, enum device_string_id key, wchar_t *string, size_t maxlen) +{ + struct udev *udev; + struct udev_device *udev_dev, *parent, *hid_dev; + struct stat s; + int ret = -1; + char *serial_number_utf8 = NULL; + char *product_name_utf8 = NULL; + + /* Create the udev object */ + udev = udev_new(); + if (!udev) { + printf("Can't create udev\n"); + return -1; + } + + /* Get the dev_t (major/minor numbers) from the file handle. */ + ret = fstat(dev->device_handle, &s); + if (-1 == ret) + return ret; + /* Open a udev device from the dev_t. 'c' means character device. */ + udev_dev = udev_device_new_from_devnum(udev, 'c', s.st_rdev); + if (udev_dev) { + hid_dev = udev_device_get_parent_with_subsystem_devtype( + udev_dev, + "hid", + NULL); + if (hid_dev) { + unsigned short dev_vid; + unsigned short dev_pid; + int bus_type; + size_t retm; + + ret = parse_uevent_info( + udev_device_get_sysattr_value(hid_dev, "uevent"), + &bus_type, + &dev_vid, + &dev_pid, + &serial_number_utf8, + &product_name_utf8); + + if (bus_type == BUS_BLUETOOTH) { + switch (key) { + case DEVICE_STRING_MANUFACTURER: + wcsncpy(string, L"", maxlen); + ret = 0; + break; + case DEVICE_STRING_PRODUCT: + retm = mbstowcs(string, product_name_utf8, maxlen); + ret = (retm == (size_t)-1)? -1: 0; + break; + case DEVICE_STRING_SERIAL: + retm = mbstowcs(string, serial_number_utf8, maxlen); + ret = (retm == (size_t)-1)? -1: 0; + break; + case DEVICE_STRING_COUNT: + default: + ret = -1; + break; + } + } + else { + /* This is a USB device. Find its parent USB Device node. */ + parent = udev_device_get_parent_with_subsystem_devtype( + udev_dev, + "usb", + "usb_device"); + if (parent) { + const char *str; + const char *key_str = NULL; + + if (key >= 0 && key < DEVICE_STRING_COUNT) { + key_str = device_string_names[key]; + } else { + ret = -1; + goto end; + } + + str = udev_device_get_sysattr_value(parent, key_str); + if (str) { + /* Convert the string from UTF-8 to wchar_t */ + retm = mbstowcs(string, str, maxlen); + ret = (retm == (size_t)-1)? -1: 0; + goto end; + } + } + } + } + } + +end: + free(serial_number_utf8); + free(product_name_utf8); + + udev_device_unref(udev_dev); + /* parent and hid_dev don't need to be (and can't be) unref'd. + I'm not sure why, but they'll throw double-free() errors. */ + udev_unref(udev); + + return ret; +} + +int HID_API_EXPORT hid_init(void) +{ + const char *locale; + + /* Set the locale if it's not set. */ + locale = setlocale(LC_CTYPE, NULL); + if (!locale) + setlocale(LC_CTYPE, ""); + + kernel_version = detect_kernel_version(); + + return 0; +} + +int HID_API_EXPORT hid_exit(void) +{ + /* Nothing to do for this in the Linux/hidraw implementation. */ + return 0; +} + + +struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id) +{ + struct udev *udev; + struct udev_enumerate *enumerate; + struct udev_list_entry *devices, *dev_list_entry; + + struct hid_device_info *root = NULL; /* return object */ + struct hid_device_info *cur_dev = NULL; + struct hid_device_info *prev_dev = NULL; /* previous device */ + + hid_init(); + + /* Create the udev object */ + udev = udev_new(); + if (!udev) { + printf("Can't create udev\n"); + return NULL; + } + + /* Create a list of the devices in the 'hidraw' subsystem. */ + enumerate = udev_enumerate_new(udev); + udev_enumerate_add_match_subsystem(enumerate, "hidraw"); + udev_enumerate_scan_devices(enumerate); + devices = udev_enumerate_get_list_entry(enumerate); + /* For each item, see if it matches the vid/pid, and if so + create a udev_device record for it */ + udev_list_entry_foreach(dev_list_entry, devices) { + const char *sysfs_path; + const char *dev_path; + const char *str; + struct udev_device *raw_dev; /* The device's hidraw udev node. */ + struct udev_device *hid_dev; /* The device's HID udev node. */ + struct udev_device *usb_dev; /* The device's USB udev node. */ + struct udev_device *intf_dev; /* The device's interface (in the USB sense). */ + unsigned short dev_vid; + unsigned short dev_pid; + char *serial_number_utf8 = NULL; + char *product_name_utf8 = NULL; + int bus_type; + int result; + + /* Get the filename of the /sys entry for the device + and create a udev_device object (dev) representing it */ + sysfs_path = udev_list_entry_get_name(dev_list_entry); + raw_dev = udev_device_new_from_syspath(udev, sysfs_path); + dev_path = udev_device_get_devnode(raw_dev); + + hid_dev = udev_device_get_parent_with_subsystem_devtype( + raw_dev, + "hid", + NULL); + + if (!hid_dev) { + /* Unable to find parent hid device. */ + goto next; + } + + result = parse_uevent_info( + udev_device_get_sysattr_value(hid_dev, "uevent"), + &bus_type, + &dev_vid, + &dev_pid, + &serial_number_utf8, + &product_name_utf8); + + if (!result) { + /* parse_uevent_info() failed for at least one field. */ + goto next; + } + + if (bus_type != BUS_USB && bus_type != BUS_BLUETOOTH) { + /* We only know how to handle USB and BT devices. */ + goto next; + } + + /* Check the VID/PID against the arguments */ + if ((vendor_id == 0x0 || vendor_id == dev_vid) && + (product_id == 0x0 || product_id == dev_pid)) { + struct hid_device_info *tmp; + + /* VID/PID match. Create the record. */ + tmp = malloc(sizeof(struct hid_device_info)); + if (cur_dev) { + cur_dev->next = tmp; + } + else { + root = tmp; + } + prev_dev = cur_dev; + cur_dev = tmp; + + /* Fill out the record */ + cur_dev->next = NULL; + cur_dev->path = dev_path? strdup(dev_path): NULL; + + /* VID/PID */ + cur_dev->vendor_id = dev_vid; + cur_dev->product_id = dev_pid; + + /* Serial Number */ + cur_dev->serial_number = utf8_to_wchar_t(serial_number_utf8); + + /* Release Number */ + cur_dev->release_number = 0x0; + + /* Interface Number */ + cur_dev->interface_number = -1; + + switch (bus_type) { + case BUS_USB: + /* The device pointed to by raw_dev contains information about + the hidraw device. In order to get information about the + USB device, get the parent device with the + subsystem/devtype pair of "usb"/"usb_device". This will + be several levels up the tree, but the function will find + it. */ + usb_dev = udev_device_get_parent_with_subsystem_devtype( + raw_dev, + "usb", + "usb_device"); + + if (!usb_dev) { + /* Free this device */ + free(cur_dev->serial_number); + free(cur_dev->path); + free(cur_dev); + + /* Take it off the device list. */ + if (prev_dev) { + prev_dev->next = NULL; + cur_dev = prev_dev; + } + else { + cur_dev = root = NULL; + } + + goto next; + } + + /* Manufacturer and Product strings */ + cur_dev->manufacturer_string = copy_udev_string(usb_dev, device_string_names[DEVICE_STRING_MANUFACTURER]); + cur_dev->product_string = copy_udev_string(usb_dev, device_string_names[DEVICE_STRING_PRODUCT]); + + /* Release Number */ + str = udev_device_get_sysattr_value(usb_dev, "bcdDevice"); + cur_dev->release_number = (str)? strtol(str, NULL, 16): 0x0; + + /* Get a handle to the interface's udev node. */ + intf_dev = udev_device_get_parent_with_subsystem_devtype( + raw_dev, + "usb", + "usb_interface"); + if (intf_dev) { + str = udev_device_get_sysattr_value(intf_dev, "bInterfaceNumber"); + cur_dev->interface_number = (str)? strtol(str, NULL, 16): -1; + } + + break; + + case BUS_BLUETOOTH: + /* Manufacturer and Product strings */ + cur_dev->manufacturer_string = wcsdup(L""); + cur_dev->product_string = utf8_to_wchar_t(product_name_utf8); + + break; + + default: + /* Unknown device type - this should never happen, as we + * check for USB and Bluetooth devices above */ + break; + } + } + + next: + free(serial_number_utf8); + free(product_name_utf8); + udev_device_unref(raw_dev); + /* hid_dev, usb_dev and intf_dev don't need to be (and can't be) + unref()d. It will cause a double-free() error. I'm not + sure why. */ + } + /* Free the enumerator and udev objects. */ + udev_enumerate_unref(enumerate); + udev_unref(udev); + + return root; +} + +void HID_API_EXPORT hid_free_enumeration(struct hid_device_info *devs) +{ + struct hid_device_info *d = devs; + while (d) { + struct hid_device_info *next = d->next; + free(d->path); + free(d->serial_number); + free(d->manufacturer_string); + free(d->product_string); + free(d); + d = next; + } +} + +hid_device * hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number) +{ + struct hid_device_info *devs, *cur_dev; + const char *path_to_open = NULL; + hid_device *handle = NULL; + + devs = hid_enumerate(vendor_id, product_id); + cur_dev = devs; + while (cur_dev) { + if (cur_dev->vendor_id == vendor_id && + cur_dev->product_id == product_id) { + if (serial_number) { + if (wcscmp(serial_number, cur_dev->serial_number) == 0) { + path_to_open = cur_dev->path; + break; + } + } + else { + path_to_open = cur_dev->path; + break; + } + } + cur_dev = cur_dev->next; + } + + if (path_to_open) { + /* Open the device */ + handle = hid_open_path(path_to_open); + } + + hid_free_enumeration(devs); + + return handle; +} + +hid_device * HID_API_EXPORT hid_open_path(const char *path) +{ + hid_device *dev = NULL; + + hid_init(); + + dev = new_hid_device(); + + /* OPEN HERE */ + dev->device_handle = open(path, O_RDWR); + + /* If we have a good handle, return it. */ + if (dev->device_handle > 0) { + + /* Get the report descriptor */ + int res, desc_size = 0; + struct hidraw_report_descriptor rpt_desc; + + memset(&rpt_desc, 0x0, sizeof(rpt_desc)); + + /* Get Report Descriptor Size */ + res = ioctl(dev->device_handle, HIDIOCGRDESCSIZE, &desc_size); + if (res < 0) + perror("HIDIOCGRDESCSIZE"); + + + /* Get Report Descriptor */ + rpt_desc.size = desc_size; + res = ioctl(dev->device_handle, HIDIOCGRDESC, &rpt_desc); + if (res < 0) { + perror("HIDIOCGRDESC"); + } else { + /* Determine if this device uses numbered reports. */ + dev->uses_numbered_reports = + uses_numbered_reports(rpt_desc.value, + rpt_desc.size); + } + + return dev; + } + else { + /* Unable to open any devices. */ + free(dev); + return NULL; + } +} + + +int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length) +{ + int bytes_written; + + bytes_written = write(dev->device_handle, data, length); + + return bytes_written; +} + + +int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) +{ + int bytes_read; + + if (milliseconds >= 0) { + /* Milliseconds is either 0 (non-blocking) or > 0 (contains + a valid timeout). In both cases we want to call poll() + and wait for data to arrive. Don't rely on non-blocking + operation (O_NONBLOCK) since some kernels don't seem to + properly report device disconnection through read() when + in non-blocking mode. */ + int ret; + struct pollfd fds; + + fds.fd = dev->device_handle; + fds.events = POLLIN; + fds.revents = 0; + ret = poll(&fds, 1, milliseconds); + if (ret == -1 || ret == 0) { + /* Error or timeout */ + return ret; + } + else { + /* Check for errors on the file descriptor. This will + indicate a device disconnection. */ + if (fds.revents & (POLLERR | POLLHUP | POLLNVAL)) + return -1; + } + } + + bytes_read = read(dev->device_handle, data, length); + if (bytes_read < 0 && (errno == EAGAIN || errno == EINPROGRESS)) + bytes_read = 0; + + if (bytes_read >= 0 && + kernel_version != 0 && + kernel_version < KERNEL_VERSION(2,6,34) && + dev->uses_numbered_reports) { + /* Work around a kernel bug. Chop off the first byte. */ + memmove(data, data+1, bytes_read); + bytes_read--; + } + + return bytes_read; +} + +int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length) +{ + return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0); +} + +int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock) +{ + /* Do all non-blocking in userspace using poll(), since it looks + like there's a bug in the kernel in some versions where + read() will not return -1 on disconnection of the USB device */ + + dev->blocking = !nonblock; + return 0; /* Success */ +} + + +int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length) +{ + int res; + + res = ioctl(dev->device_handle, HIDIOCSFEATURE(length), data); + if (res < 0) + perror("ioctl (SFEATURE)"); + + return res; +} + +int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length) +{ + int res; + + res = ioctl(dev->device_handle, HIDIOCGFEATURE(length), data); + if (res < 0) + perror("ioctl (GFEATURE)"); + + + return res; +} + + +void HID_API_EXPORT hid_close(hid_device *dev) +{ + if (!dev) + return; + close(dev->device_handle); + free(dev); +} + + +int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + return get_device_string(dev, DEVICE_STRING_MANUFACTURER, string, maxlen); +} + +int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + return get_device_string(dev, DEVICE_STRING_PRODUCT, string, maxlen); +} + +int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + return get_device_string(dev, DEVICE_STRING_SERIAL, string, maxlen); +} + +int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen) +{ + return -1; +} + + +HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev) +{ + return NULL; +} diff --git a/src/hidapi/mac/hid.c b/src/hidapi/mac/hid.c new file mode 100644 index 000000000..ca10a9cca --- /dev/null +++ b/src/hidapi/mac/hid.c @@ -0,0 +1,1121 @@ +/******************************************************* + HIDAPI - Multi-Platform library for + communication with HID devices. + + Alan Ott + Signal 11 Software + + 2010-07-03 + + Copyright 2010, 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 . +********************************************************/ + +/* See Apple Technical Note TN2187 for details on IOHidManager. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hidapi.h" + +/* Barrier implementation because Mac OSX doesn't have pthread_barrier. + It also doesn't have clock_gettime(). So much for POSIX and SUSv2. + 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; + } +} + +static int return_data(hid_device *dev, unsigned char *data, size_t length); + +/* 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_ { + IOHIDDeviceRef device_handle; + int blocking; + int uses_numbered_reports; + int disconnected; + CFStringRef run_loop_mode; + CFRunLoopRef run_loop; + CFRunLoopSourceRef source; + uint8_t *input_report_buf; + CFIndex max_input_report_len; + struct input_report *input_reports; + + pthread_t thread; + pthread_mutex_t mutex; /* Protects input_reports */ + pthread_cond_t condition; + pthread_barrier_t barrier; /* Ensures correct startup sequence */ + pthread_barrier_t shutdown_barrier; /* Ensures correct shutdown sequence */ + int shutdown_thread; +}; + +static hid_device *new_hid_device(void) +{ + hid_device *dev = calloc(1, sizeof(hid_device)); + dev->device_handle = NULL; + dev->blocking = 1; + dev->uses_numbered_reports = 0; + dev->disconnected = 0; + dev->run_loop_mode = NULL; + dev->run_loop = NULL; + dev->source = NULL; + dev->input_report_buf = NULL; + dev->input_reports = NULL; + dev->shutdown_thread = 0; + + /* Thread objects */ + pthread_mutex_init(&dev->mutex, NULL); + pthread_cond_init(&dev->condition, NULL); + pthread_barrier_init(&dev->barrier, NULL, 2); + pthread_barrier_init(&dev->shutdown_barrier, NULL, 2); + + return dev; +} + +static void free_hid_device(hid_device *dev) +{ + if (!dev) + return; + + /* Delete any input reports still left over. */ + struct input_report *rpt = dev->input_reports; + while (rpt) { + struct input_report *next = rpt->next; + free(rpt->data); + free(rpt); + rpt = next; + } + + /* Free the string and the report buffer. The check for NULL + is necessary here as CFRelease() doesn't handle NULL like + free() and others do. */ + if (dev->run_loop_mode) + CFRelease(dev->run_loop_mode); + if (dev->source) + CFRelease(dev->source); + free(dev->input_report_buf); + + /* Clean up the thread objects */ + pthread_barrier_destroy(&dev->shutdown_barrier); + pthread_barrier_destroy(&dev->barrier); + pthread_cond_destroy(&dev->condition); + pthread_mutex_destroy(&dev->mutex); + + /* Free the structure itself. */ + free(dev); +} + +static IOHIDManagerRef hid_mgr = 0x0; + + +#if 0 +static void register_error(hid_device *dev, const char *op) +{ + +} +#endif + + +static int32_t get_int_property(IOHIDDeviceRef device, CFStringRef key) +{ + CFTypeRef ref; + int32_t value; + + ref = IOHIDDeviceGetProperty(device, key); + if (ref) { + if (CFGetTypeID(ref) == CFNumberGetTypeID()) { + CFNumberGetValue((CFNumberRef) ref, kCFNumberSInt32Type, &value); + return value; + } + } + return 0; +} + +static unsigned short get_vendor_id(IOHIDDeviceRef device) +{ + return get_int_property(device, CFSTR(kIOHIDVendorIDKey)); +} + +static unsigned short get_product_id(IOHIDDeviceRef device) +{ + return get_int_property(device, CFSTR(kIOHIDProductIDKey)); +} + +static int32_t get_max_report_length(IOHIDDeviceRef device) +{ + return get_int_property(device, CFSTR(kIOHIDMaxInputReportSizeKey)); +} + +static int get_string_property(IOHIDDeviceRef device, CFStringRef prop, wchar_t *buf, size_t len) +{ + CFStringRef str; + + if (!len) + return 0; + + str = IOHIDDeviceGetProperty(device, prop); + + buf[0] = 0; + + if (str) { + CFIndex str_len = CFStringGetLength(str); + CFRange range; + CFIndex used_buf_len; + CFIndex chars_copied; + + len --; + + range.location = 0; + range.length = ((size_t)str_len > len)? len: (size_t)str_len; + chars_copied = CFStringGetBytes(str, + range, + kCFStringEncodingUTF32LE, + (char)'?', + FALSE, + (UInt8*)buf, + len * sizeof(wchar_t), + &used_buf_len); + + if (chars_copied == len) + buf[len] = 0; /* len is decremented above */ + else + buf[chars_copied] = 0; + + return 0; + } + else + return -1; + +} + +static int get_serial_number(IOHIDDeviceRef device, wchar_t *buf, size_t len) +{ + return get_string_property(device, CFSTR(kIOHIDSerialNumberKey), buf, len); +} + +static int get_manufacturer_string(IOHIDDeviceRef device, wchar_t *buf, size_t len) +{ + return get_string_property(device, CFSTR(kIOHIDManufacturerKey), buf, len); +} + +static int get_product_string(IOHIDDeviceRef device, wchar_t *buf, size_t len) +{ + return get_string_property(device, CFSTR(kIOHIDProductKey), buf, len); +} + + +/* Implementation of wcsdup() for Mac. */ +static wchar_t *dup_wcs(const wchar_t *s) +{ + size_t len = wcslen(s); + wchar_t *ret = malloc((len+1)*sizeof(wchar_t)); + wcscpy(ret, s); + + return ret; +} + +/* hidapi_IOHIDDeviceGetService() + * + * Return the io_service_t corresponding to a given IOHIDDeviceRef, either by: + * - on OS X 10.6 and above, calling IOHIDDeviceGetService() + * - on OS X 10.5, extract it from the IOHIDDevice struct + */ +static io_service_t hidapi_IOHIDDeviceGetService(IOHIDDeviceRef device) +{ + static void *iokit_framework = NULL; + static io_service_t (*dynamic_IOHIDDeviceGetService)(IOHIDDeviceRef device) = NULL; + + /* Use dlopen()/dlsym() to get a pointer to IOHIDDeviceGetService() if it exists. + * If any of these steps fail, dynamic_IOHIDDeviceGetService will be left NULL + * and the fallback method will be used. + */ + if (iokit_framework == NULL) { + iokit_framework = dlopen("/System/Library/IOKit.framework/IOKit", RTLD_LAZY); + + if (iokit_framework != NULL) + dynamic_IOHIDDeviceGetService = dlsym(iokit_framework, "IOHIDDeviceGetService"); + } + + if (dynamic_IOHIDDeviceGetService != NULL) { + /* Running on OS X 10.6 and above: IOHIDDeviceGetService() exists */ + return dynamic_IOHIDDeviceGetService(device); + } + else + { + /* Running on OS X 10.5: IOHIDDeviceGetService() doesn't exist. + * + * Be naughty and pull the service out of the IOHIDDevice. + * IOHIDDevice is an opaque struct not exposed to applications, but its + * layout is stable through all available versions of OS X. + * Tested and working on OS X 10.5.8 i386, x86_64, and ppc. + */ + struct IOHIDDevice_internal { + /* The first field of the IOHIDDevice struct is a + * CFRuntimeBase (which is a private CF struct). + * + * a, b, and c are the 3 fields that make up a CFRuntimeBase. + * See http://opensource.apple.com/source/CF/CF-476.18/CFRuntime.h + * + * The second field of the IOHIDDevice is the io_service_t we're looking for. + */ + uintptr_t a; + uint8_t b[4]; +#if __LP64__ + uint32_t c; +#endif + io_service_t service; + }; + struct IOHIDDevice_internal *tmp = (struct IOHIDDevice_internal *)device; + + return tmp->service; + } +} + +/* Initialize the IOHIDManager. Return 0 for success and -1 for failure. */ +static int init_hid_manager(void) +{ + /* Initialize all the HID Manager Objects */ + hid_mgr = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); + if (hid_mgr) { + IOHIDManagerSetDeviceMatching(hid_mgr, NULL); + IOHIDManagerScheduleWithRunLoop(hid_mgr, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + return 0; + } + + return -1; +} + +/* Initialize the IOHIDManager if necessary. This is the public function, and + it is safe to call this function repeatedly. Return 0 for success and -1 + for failure. */ +int HID_API_EXPORT hid_init(void) +{ + if (!hid_mgr) { + return init_hid_manager(); + } + + /* Already initialized. */ + return 0; +} + +int HID_API_EXPORT hid_exit(void) +{ + if (hid_mgr) { + /* Close the HID manager. */ + IOHIDManagerClose(hid_mgr, kIOHIDOptionsTypeNone); + CFRelease(hid_mgr); + hid_mgr = NULL; + } + + return 0; +} + +static void process_pending_events(void) { + SInt32 res; + do { + res = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.001, FALSE); + } while(res != kCFRunLoopRunFinished && res != kCFRunLoopRunTimedOut); +} + +struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id) +{ + struct hid_device_info *root = NULL; /* return object */ + struct hid_device_info *cur_dev = NULL; + CFIndex num_devices; + int i; + + /* Set up the HID Manager if it hasn't been done */ + if (hid_init() < 0) + return NULL; + + /* give the IOHIDManager a chance to update itself */ + process_pending_events(); + + /* Get a list of the Devices */ + IOHIDManagerSetDeviceMatching(hid_mgr, NULL); + CFSetRef device_set = IOHIDManagerCopyDevices(hid_mgr); + + /* Convert the list into a C array so we can iterate easily. */ + num_devices = CFSetGetCount(device_set); + IOHIDDeviceRef *device_array = calloc(num_devices, sizeof(IOHIDDeviceRef)); + CFSetGetValues(device_set, (const void **) device_array); + + /* Iterate over each device, making an entry for it. */ + for (i = 0; i < num_devices; i++) { + unsigned short dev_vid; + unsigned short dev_pid; + #define BUF_LEN 256 + wchar_t buf[BUF_LEN]; + + IOHIDDeviceRef dev = device_array[i]; + + if (!dev) { + continue; + } + dev_vid = get_vendor_id(dev); + dev_pid = get_product_id(dev); + + /* 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; + bool is_usb_hid; /* Is this an actual HID usb device */ + io_object_t iokit_dev; + kern_return_t res; + io_string_t path; + + /* VID/PID match. Create the record. */ + tmp = malloc(sizeof(struct hid_device_info)); + if (cur_dev) { + cur_dev->next = tmp; + } + else { + root = tmp; + } + cur_dev = tmp; + + is_usb_hid = get_int_property(dev, CFSTR(kUSBInterfaceClass)) == kUSBHIDClass; + + /* Get the Usage Page and Usage for this device. */ + cur_dev->usage_page = get_int_property(dev, CFSTR(kIOHIDPrimaryUsagePageKey)); + cur_dev->usage = get_int_property(dev, CFSTR(kIOHIDPrimaryUsageKey)); + + /* Fill out the record */ + cur_dev->next = NULL; + + /* Fill in the path (IOService plane) */ + iokit_dev = hidapi_IOHIDDeviceGetService(dev); + res = IORegistryEntryGetPath(iokit_dev, kIOServicePlane, path); + if (res == KERN_SUCCESS) + cur_dev->path = strdup(path); + else + cur_dev->path = strdup(""); + + /* Serial Number */ + get_serial_number(dev, buf, BUF_LEN); + cur_dev->serial_number = dup_wcs(buf); + + /* Manufacturer and Product strings */ + get_manufacturer_string(dev, buf, BUF_LEN); + cur_dev->manufacturer_string = dup_wcs(buf); + get_product_string(dev, buf, BUF_LEN); + cur_dev->product_string = dup_wcs(buf); + + /* VID/PID */ + cur_dev->vendor_id = dev_vid; + cur_dev->product_id = dev_pid; + + /* Release Number */ + cur_dev->release_number = get_int_property(dev, CFSTR(kIOHIDVersionNumberKey)); + + /* We can only retrieve the interface number for USB HID devices. + * IOKit always seems to return 0 when querying a standard USB device + * for its interface. */ + if (is_usb_hid) { + /* Get the interface number */ + cur_dev->interface_number = get_int_property(dev, CFSTR(kUSBInterfaceNumber)); + } else { + cur_dev->interface_number = -1; + } + } + } + + free(device_array); + CFRelease(device_set); + + return root; +} + +void HID_API_EXPORT hid_free_enumeration(struct hid_device_info *devs) +{ + /* This function is identical to the Linux version. Platform independent. */ + struct hid_device_info *d = devs; + while (d) { + struct hid_device_info *next = d->next; + free(d->path); + free(d->serial_number); + free(d->manufacturer_string); + free(d->product_string); + free(d); + d = next; + } +} + +hid_device * HID_API_EXPORT hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number) +{ + /* This function is identical to the Linux version. Platform independent. */ + struct hid_device_info *devs, *cur_dev; + const char *path_to_open = NULL; + hid_device * handle = NULL; + + devs = hid_enumerate(vendor_id, product_id); + cur_dev = devs; + while (cur_dev) { + if (cur_dev->vendor_id == vendor_id && + cur_dev->product_id == product_id) { + if (serial_number) { + if (wcscmp(serial_number, cur_dev->serial_number) == 0) { + path_to_open = cur_dev->path; + break; + } + } + else { + path_to_open = cur_dev->path; + break; + } + } + cur_dev = cur_dev->next; + } + + if (path_to_open) { + /* Open the device */ + handle = hid_open_path(path_to_open); + } + + hid_free_enumeration(devs); + + return handle; +} + +static void hid_device_removal_callback(void *context, IOReturn result, + void *sender) +{ + /* Stop the Run Loop for this device. */ + hid_device *d = context; + + d->disconnected = 1; + CFRunLoopStop(d->run_loop); +} + +/* The Run Loop calls this function for each input report received. + This function puts the data into a linked list to be picked up by + hid_read(). */ +static void hid_report_callback(void *context, IOReturn result, void *sender, + IOHIDReportType report_type, uint32_t report_id, + uint8_t *report, CFIndex report_length) +{ + struct input_report *rpt; + hid_device *dev = context; + + /* Make a new Input Report object */ + rpt = calloc(1, sizeof(struct input_report)); + rpt->data = calloc(1, report_length); + memcpy(rpt->data, report, report_length); + rpt->len = report_length; + rpt->next = NULL; + + /* Lock this section */ + 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; + } + 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); + } + } + + /* Signal a waiting thread that there is data. */ + pthread_cond_signal(&dev->condition); + + /* Unlock */ + pthread_mutex_unlock(&dev->mutex); + +} + +/* This gets called when the read_thread's run loop gets signaled by + hid_close(), and serves to stop the read_thread's run loop. */ +static void perform_signal_callback(void *context) +{ + hid_device *dev = context; + CFRunLoopStop(dev->run_loop); /*TODO: CFRunLoopGetCurrent()*/ +} + +static void *read_thread(void *param) +{ + hid_device *dev = param; + SInt32 code; + + /* Move the device's run loop to this thread. */ + IOHIDDeviceScheduleWithRunLoop(dev->device_handle, CFRunLoopGetCurrent(), dev->run_loop_mode); + + /* Create the RunLoopSource which is used to signal the + event loop to stop when hid_close() is called. */ + CFRunLoopSourceContext ctx; + memset(&ctx, 0, sizeof(ctx)); + ctx.version = 0; + ctx.info = dev; + ctx.perform = &perform_signal_callback; + dev->source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0/*order*/, &ctx); + CFRunLoopAddSource(CFRunLoopGetCurrent(), dev->source, dev->run_loop_mode); + + /* Store off the Run Loop so it can be stopped from hid_close() + and on device disconnection. */ + dev->run_loop = CFRunLoopGetCurrent(); + + /* Notify the main thread that the read thread is up and running. */ + pthread_barrier_wait(&dev->barrier); + + /* Run the Event Loop. CFRunLoopRunInMode() will dispatch HID input + reports into the hid_report_callback(). */ + while (!dev->shutdown_thread && !dev->disconnected) { + code = CFRunLoopRunInMode(dev->run_loop_mode, 1000/*sec*/, FALSE); + /* Return if the device has been disconnected */ + if (code == kCFRunLoopRunFinished) { + dev->disconnected = 1; + break; + } + + + /* Break if The Run Loop returns Finished or Stopped. */ + if (code != kCFRunLoopRunTimedOut && + code != kCFRunLoopRunHandledSource) { + /* There was some kind of error. Setting + shutdown seems to make sense, but + there may be something else more appropriate */ + dev->shutdown_thread = 1; + break; + } + } + + /* 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); + + /* Wait here until hid_close() is called and makes it past + the call to CFRunLoopWakeUp(). This thread still needs to + be valid when that function is called on the other thread. */ + pthread_barrier_wait(&dev->shutdown_barrier); + + return NULL; +} + +/* hid_open_path() + * + * path must be a valid path to an IOHIDDevice in the IOService plane + * Example: "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/EHC1@1D,7/AppleUSBEHCI/PLAYSTATION(R)3 Controller@fd120000/IOUSBInterface@0/IOUSBHIDDriver" + */ +hid_device * HID_API_EXPORT hid_open_path(const char *path) +{ + hid_device *dev = NULL; + io_registry_entry_t entry = MACH_PORT_NULL; + + dev = new_hid_device(); + + /* Set up the HID Manager if it hasn't been done */ + if (hid_init() < 0) + return NULL; + + /* Get the IORegistry entry for the given path */ + entry = IORegistryEntryFromPath(kIOMasterPortDefault, path); + if (entry == MACH_PORT_NULL) { + /* Path wasn't valid (maybe device was removed?) */ + goto return_error; + } + + /* Create an IOHIDDevice for the entry */ + dev->device_handle = IOHIDDeviceCreate(kCFAllocatorDefault, entry); + if (dev->device_handle == NULL) { + /* Error creating the HID device */ + goto return_error; + } + + /* Open the IOHIDDevice */ + IOReturn ret = IOHIDDeviceOpen(dev->device_handle, kIOHIDOptionsTypeSeizeDevice); + if (ret == kIOReturnSuccess) { + char str[32]; + + /* Create the buffers for receiving data */ + dev->max_input_report_len = (CFIndex) get_max_report_length(dev->device_handle); + dev->input_report_buf = calloc(dev->max_input_report_len, sizeof(uint8_t)); + + /* Create the Run Loop Mode for this device. + printing the reference seems to work. */ + sprintf(str, "HIDAPI_%p", dev->device_handle); + dev->run_loop_mode = + CFStringCreateWithCString(NULL, str, kCFStringEncodingASCII); + + /* Attach the device to a Run Loop */ + IOHIDDeviceRegisterInputReportCallback( + dev->device_handle, dev->input_report_buf, dev->max_input_report_len, + &hid_report_callback, dev); + IOHIDDeviceRegisterRemovalCallback(dev->device_handle, hid_device_removal_callback, dev); + + /* Start the read thread */ + pthread_create(&dev->thread, NULL, read_thread, dev); + + /* Wait here for the read thread to be initialized. */ + pthread_barrier_wait(&dev->barrier); + + IOObjectRelease(entry); + return dev; + } + else { + goto return_error; + } + +return_error: + if (dev->device_handle != NULL) + CFRelease(dev->device_handle); + + if (entry != MACH_PORT_NULL) + IOObjectRelease(entry); + + free_hid_device(dev); + return NULL; +} + +static int set_report(hid_device *dev, IOHIDReportType type, const unsigned char *data, size_t length) +{ + const unsigned char *data_to_send; + size_t length_to_send; + IOReturn res; + + /* Return if the device has been disconnected. */ + if (dev->disconnected) + return -1; + + if (data[0] == 0x0) { + /* Not using numbered Reports. + Don't send the report number. */ + data_to_send = data+1; + length_to_send = length-1; + } + else { + /* Using numbered Reports. + Send the Report Number */ + data_to_send = data; + length_to_send = length; + } + + if (!dev->disconnected) { + res = IOHIDDeviceSetReport(dev->device_handle, + type, + data[0], /* Report ID*/ + data_to_send, length_to_send); + + if (res == kIOReturnSuccess) { + return length; + } + else + return -1; + } + + return -1; +} + +int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length) +{ + return set_report(dev, kIOHIDReportTypeOutput, data, length); +} + +/* Helper function, so that this isn't duplicated in hid_read(). */ +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; + memcpy(data, rpt->data, len); + dev->input_reports = rpt->next; + free(rpt->data); + free(rpt); + return len; +} + +static int cond_wait(const hid_device *dev, pthread_cond_t *cond, pthread_mutex_t *mutex) +{ + while (!dev->input_reports) { + int res = pthread_cond_wait(cond, mutex); + if (res != 0) + return res; + + /* A res of 0 means we may have been signaled or it may + be a spurious wakeup. Check to see that there's acutally + data in the queue before returning, and if not, go back + to sleep. See the pthread_cond_timedwait() man page for + details. */ + + if (dev->shutdown_thread || dev->disconnected) + return -1; + } + + return 0; +} + +static int cond_timedwait(const hid_device *dev, pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime) +{ + while (!dev->input_reports) { + int res = pthread_cond_timedwait(cond, mutex, abstime); + if (res != 0) + return res; + + /* A res of 0 means we may have been signaled or it may + be a spurious wakeup. Check to see that there's acutally + data in the queue before returning, and if not, go back + to sleep. See the pthread_cond_timedwait() man page for + details. */ + + if (dev->shutdown_thread || dev->disconnected) + return -1; + } + + return 0; + +} + +int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) +{ + int bytes_read = -1; + + /* Lock the access to the report list. */ + pthread_mutex_lock(&dev->mutex); + + /* 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; + } + + /* Return if the device has been disconnected. */ + if (dev->disconnected) { + bytes_read = -1; + goto ret; + } + + if (dev->shutdown_thread) { + /* This means the device has been closed (or there + has been an error. An error code of -1 should + be returned. */ + bytes_read = -1; + goto ret; + } + + /* There is no data. Go to sleep and wait for data. */ + + if (milliseconds == -1) { + /* Blocking */ + int res; + res = cond_wait(dev, &dev->condition, &dev->mutex); + if (res == 0) + bytes_read = return_data(dev, data, length); + else { + /* There was an error, or a device disconnection. */ + bytes_read = -1; + } + } + else if (milliseconds > 0) { + /* Non-blocking, but called with timeout. */ + int res; + struct timespec ts; + struct timeval tv; + gettimeofday(&tv, NULL); + TIMEVAL_TO_TIMESPEC(&tv, &ts); + ts.tv_sec += milliseconds / 1000; + ts.tv_nsec += (milliseconds % 1000) * 1000000; + if (ts.tv_nsec >= 1000000000L) { + ts.tv_sec++; + ts.tv_nsec -= 1000000000L; + } + + res = cond_timedwait(dev, &dev->condition, &dev->mutex, &ts); + if (res == 0) + bytes_read = return_data(dev, data, length); + else if (res == ETIMEDOUT) + bytes_read = 0; + else + bytes_read = -1; + } + else { + /* Purely non-blocking */ + bytes_read = 0; + } + +ret: + /* Unlock */ + pthread_mutex_unlock(&dev->mutex); + 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) +{ + /* All Nonblocking operation is handled by the library. */ + dev->blocking = !nonblock; + + return 0; +} + +int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length) +{ + return set_report(dev, kIOHIDReportTypeFeature, data, length); +} + +int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length) +{ + CFIndex len = length - 1; + IOReturn res; + + /* Return if the device has been unplugged. */ + if (dev->disconnected) + return -1; + + res = IOHIDDeviceGetReport(dev->device_handle, + kIOHIDReportTypeFeature, + data[0], /* Report ID */ + data + 1, &len); + if (res == kIOReturnSuccess) + return len + 1; + else + return -1; +} + + +void HID_API_EXPORT hid_close(hid_device *dev) +{ + if (!dev) + return; + + /* Disconnect the report callback before close. */ + if (!dev->disconnected) { + IOHIDDeviceRegisterInputReportCallback( + dev->device_handle, dev->input_report_buf, dev->max_input_report_len, + NULL, dev); + IOHIDDeviceRegisterRemovalCallback(dev->device_handle, NULL, dev); + IOHIDDeviceUnscheduleFromRunLoop(dev->device_handle, dev->run_loop, dev->run_loop_mode); + IOHIDDeviceScheduleWithRunLoop(dev->device_handle, CFRunLoopGetMain(), kCFRunLoopDefaultMode); + } + + /* Cause read_thread() to stop. */ + dev->shutdown_thread = 1; + + /* Wake up the run thread's event loop so that the thread can exit. */ + CFRunLoopSourceSignal(dev->source); + CFRunLoopWakeUp(dev->run_loop); + + /* Notify the read thread that it can shut down now. */ + pthread_barrier_wait(&dev->shutdown_barrier); + + /* Wait for read_thread() to end. */ + pthread_join(dev->thread, NULL); + + /* Close the OS handle to the device, but only if it's not + been unplugged. If it's been unplugged, then calling + IOHIDDeviceClose() will crash. */ + if (!dev->disconnected) { + IOHIDDeviceClose(dev->device_handle, kIOHIDOptionsTypeSeizeDevice); + } + + /* 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); + CFRelease(dev->device_handle); + + free_hid_device(dev); +} + +int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + return get_manufacturer_string(dev->device_handle, string, maxlen); +} + +int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + return get_product_string(dev->device_handle, string, maxlen); +} + +int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + return get_serial_number(dev->device_handle, string, maxlen); +} + +int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen) +{ + /* TODO: */ + + return 0; +} + + +HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev) +{ + /* TODO: */ + + return NULL; +} + + + + + + + +#if 0 +static int32_t get_location_id(IOHIDDeviceRef device) +{ + return get_int_property(device, CFSTR(kIOHIDLocationIDKey)); +} + +static int32_t get_usage(IOHIDDeviceRef device) +{ + int32_t res; + res = get_int_property(device, CFSTR(kIOHIDDeviceUsageKey)); + if (!res) + res = get_int_property(device, CFSTR(kIOHIDPrimaryUsageKey)); + return res; +} + +static int32_t get_usage_page(IOHIDDeviceRef device) +{ + int32_t res; + res = get_int_property(device, CFSTR(kIOHIDDeviceUsagePageKey)); + if (!res) + res = get_int_property(device, CFSTR(kIOHIDPrimaryUsagePageKey)); + return res; +} + +static int get_transport(IOHIDDeviceRef device, wchar_t *buf, size_t len) +{ + return get_string_property(device, CFSTR(kIOHIDTransportKey), buf, len); +} + + +int main(void) +{ + IOHIDManagerRef mgr; + int i; + + mgr = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); + IOHIDManagerSetDeviceMatching(mgr, NULL); + IOHIDManagerOpen(mgr, kIOHIDOptionsTypeNone); + + CFSetRef device_set = IOHIDManagerCopyDevices(mgr); + + CFIndex num_devices = CFSetGetCount(device_set); + IOHIDDeviceRef *device_array = calloc(num_devices, sizeof(IOHIDDeviceRef)); + CFSetGetValues(device_set, (const void **) device_array); + + for (i = 0; i < num_devices; i++) { + IOHIDDeviceRef dev = device_array[i]; + printf("Device: %p\n", dev); + printf(" %04hx %04hx\n", get_vendor_id(dev), get_product_id(dev)); + + wchar_t serial[256], buf[256]; + char cbuf[256]; + get_serial_number(dev, serial, 256); + + + printf(" Serial: %ls\n", serial); + printf(" Loc: %ld\n", get_location_id(dev)); + get_transport(dev, buf, 256); + printf(" Trans: %ls\n", buf); + make_path(dev, cbuf, 256); + printf(" Path: %s\n", cbuf); + + } + + return 0; +} +#endif diff --git a/src/hidapi/win/hid.c b/src/hidapi/win/hid.c new file mode 100644 index 000000000..4a71e2552 --- /dev/null +++ b/src/hidapi/win/hid.c @@ -0,0 +1,956 @@ +/******************************************************* + HIDAPI - Multi-Platform library for + communication with HID devices. + + Alan Ott + Signal 11 Software + + 8/22/2009 + + Copyright 2009, All Rights Reserved. + + At the discretion of the user of this library, + this software may be licensed under the terms of the + GNU General Public License v3, a BSD-Style license, or the + original HIDAPI license as outlined in the LICENSE.txt, + LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt + files located at the root of the source distribution. + These files may also be found in the public source + code repository located at: + http://github.com/signal11/hidapi . +********************************************************/ + +#include + +#ifndef _NTDEF_ +typedef LONG NTSTATUS; +#endif + +#ifdef __MINGW32__ +#include +#include +#endif + +#ifdef __CYGWIN__ +#include +#define _wcsdup wcsdup +#endif + +/* The maximum number of characters that can be passed into the + HidD_Get*String() functions without it failing.*/ +#define MAX_STRING_WCHARS 0xFFF + +/*#define HIDAPI_USE_DDK*/ + +#ifdef __cplusplus +extern "C" { +#endif + #include + #include + #ifdef HIDAPI_USE_DDK + #include + #endif + + /* Copied from inc/ddk/hidclass.h, part of the Windows DDK. */ + #define HID_OUT_CTL_CODE(id) \ + CTL_CODE(FILE_DEVICE_KEYBOARD, (id), METHOD_OUT_DIRECT, FILE_ANY_ACCESS) + #define IOCTL_HID_GET_FEATURE HID_OUT_CTL_CODE(100) + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#include +#include + + +#include "hidapi.h" + +#undef MIN +#define MIN(x,y) ((x) < (y)? (x): (y)) + +#ifdef _MSC_VER + /* Thanks Microsoft, but I know how to use strncpy(). */ + #pragma warning(disable:4996) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef HIDAPI_USE_DDK + /* Since we're not building with the DDK, and the HID header + files aren't part of the SDK, we have to define all this + stuff here. In lookup_functions(), the function pointers + defined below are set. */ + typedef struct _HIDD_ATTRIBUTES{ + ULONG Size; + USHORT VendorID; + USHORT ProductID; + USHORT VersionNumber; + } HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES; + + typedef USHORT USAGE; + typedef struct _HIDP_CAPS { + USAGE Usage; + USAGE UsagePage; + USHORT InputReportByteLength; + USHORT OutputReportByteLength; + USHORT FeatureReportByteLength; + USHORT Reserved[17]; + USHORT fields_not_used_by_hidapi[10]; + } HIDP_CAPS, *PHIDP_CAPS; + typedef void* PHIDP_PREPARSED_DATA; + #define HIDP_STATUS_SUCCESS 0x110000 + + typedef BOOLEAN (__stdcall *HidD_GetAttributes_)(HANDLE device, PHIDD_ATTRIBUTES attrib); + typedef BOOLEAN (__stdcall *HidD_GetSerialNumberString_)(HANDLE device, PVOID buffer, ULONG buffer_len); + typedef BOOLEAN (__stdcall *HidD_GetManufacturerString_)(HANDLE handle, PVOID buffer, ULONG buffer_len); + typedef BOOLEAN (__stdcall *HidD_GetProductString_)(HANDLE handle, PVOID buffer, ULONG buffer_len); + typedef BOOLEAN (__stdcall *HidD_SetFeature_)(HANDLE handle, PVOID data, ULONG length); + typedef BOOLEAN (__stdcall *HidD_GetFeature_)(HANDLE handle, PVOID data, ULONG length); + typedef BOOLEAN (__stdcall *HidD_GetIndexedString_)(HANDLE handle, ULONG string_index, PVOID buffer, ULONG buffer_len); + typedef BOOLEAN (__stdcall *HidD_GetPreparsedData_)(HANDLE handle, PHIDP_PREPARSED_DATA *preparsed_data); + typedef BOOLEAN (__stdcall *HidD_FreePreparsedData_)(PHIDP_PREPARSED_DATA preparsed_data); + typedef NTSTATUS (__stdcall *HidP_GetCaps_)(PHIDP_PREPARSED_DATA preparsed_data, HIDP_CAPS *caps); + typedef BOOLEAN (__stdcall *HidD_SetNumInputBuffers_)(HANDLE handle, ULONG number_buffers); + + static HidD_GetAttributes_ HidD_GetAttributes; + static HidD_GetSerialNumberString_ HidD_GetSerialNumberString; + static HidD_GetManufacturerString_ HidD_GetManufacturerString; + static HidD_GetProductString_ HidD_GetProductString; + static HidD_SetFeature_ HidD_SetFeature; + static HidD_GetFeature_ HidD_GetFeature; + static HidD_GetIndexedString_ HidD_GetIndexedString; + static HidD_GetPreparsedData_ HidD_GetPreparsedData; + static HidD_FreePreparsedData_ HidD_FreePreparsedData; + static HidP_GetCaps_ HidP_GetCaps; + static HidD_SetNumInputBuffers_ HidD_SetNumInputBuffers; + + static HMODULE lib_handle = NULL; + static BOOLEAN initialized = FALSE; +#endif /* HIDAPI_USE_DDK */ + +struct hid_device_ { + HANDLE device_handle; + BOOL blocking; + USHORT output_report_length; + size_t input_report_length; + void *last_error_str; + DWORD last_error_num; + BOOL read_pending; + char *read_buf; + OVERLAPPED ol; +}; + +static hid_device *new_hid_device() +{ + hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device)); + dev->device_handle = INVALID_HANDLE_VALUE; + dev->blocking = TRUE; + dev->output_report_length = 0; + dev->input_report_length = 0; + dev->last_error_str = NULL; + dev->last_error_num = 0; + dev->read_pending = FALSE; + dev->read_buf = NULL; + memset(&dev->ol, 0, sizeof(dev->ol)); + dev->ol.hEvent = CreateEvent(NULL, FALSE, FALSE /*initial state f=nonsignaled*/, NULL); + + return dev; +} + +static void free_hid_device(hid_device *dev) +{ + CloseHandle(dev->ol.hEvent); + CloseHandle(dev->device_handle); + LocalFree(dev->last_error_str); + free(dev->read_buf); + free(dev); +} + +static void register_error(hid_device *dev, const char *op) +{ + WCHAR *ptr, *msg; + + FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPVOID)&msg, 0/*sz*/, + NULL); + + /* Get rid of the CR and LF that FormatMessage() sticks at the + end of the message. Thanks Microsoft! */ + ptr = msg; + while (*ptr) { + if (*ptr == '\r') { + *ptr = 0x0000; + break; + } + ptr++; + } + + /* Store the message off in the Device entry so that + the hid_error() function can pick it up. */ + LocalFree(dev->last_error_str); + dev->last_error_str = msg; +} + +#ifndef HIDAPI_USE_DDK +static int lookup_functions() +{ + lib_handle = LoadLibraryA("hid.dll"); + if (lib_handle) { +#define RESOLVE(x) x = (x##_)GetProcAddress(lib_handle, #x); if (!x) return -1; + RESOLVE(HidD_GetAttributes); + RESOLVE(HidD_GetSerialNumberString); + RESOLVE(HidD_GetManufacturerString); + RESOLVE(HidD_GetProductString); + RESOLVE(HidD_SetFeature); + RESOLVE(HidD_GetFeature); + RESOLVE(HidD_GetIndexedString); + RESOLVE(HidD_GetPreparsedData); + RESOLVE(HidD_FreePreparsedData); + RESOLVE(HidP_GetCaps); + RESOLVE(HidD_SetNumInputBuffers); +#undef RESOLVE + } + else + return -1; + + return 0; +} +#endif + +static HANDLE open_device(const char *path, BOOL open_rw) +{ + HANDLE handle; + DWORD desired_access = (open_rw)? (GENERIC_WRITE | GENERIC_READ): 0; + DWORD share_mode = FILE_SHARE_READ|FILE_SHARE_WRITE; + + handle = CreateFileA(path, + desired_access, + share_mode, + NULL, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED,/*FILE_ATTRIBUTE_NORMAL,*/ + 0); + + return handle; +} + +int HID_API_EXPORT hid_init(void) +{ +#ifndef HIDAPI_USE_DDK + if (!initialized) { + if (lookup_functions() < 0) { + hid_exit(); + return -1; + } + initialized = TRUE; + } +#endif + return 0; +} + +int HID_API_EXPORT hid_exit(void) +{ +#ifndef HIDAPI_USE_DDK + if (lib_handle) + FreeLibrary(lib_handle); + lib_handle = NULL; + initialized = FALSE; +#endif + return 0; +} + +struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id) +{ + BOOL res; + struct hid_device_info *root = NULL; /* return object */ + struct hid_device_info *cur_dev = NULL; + + /* Windows objects for interacting with the driver. */ + GUID InterfaceClassGuid = {0x4d1e55b2, 0xf16f, 0x11cf, {0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30} }; + SP_DEVINFO_DATA devinfo_data; + SP_DEVICE_INTERFACE_DATA device_interface_data; + SP_DEVICE_INTERFACE_DETAIL_DATA_A *device_interface_detail_data = NULL; + HDEVINFO device_info_set = INVALID_HANDLE_VALUE; + int device_index = 0; + int i; + + if (hid_init() < 0) + return NULL; + + /* Initialize the Windows objects. */ + memset(&devinfo_data, 0x0, sizeof(devinfo_data)); + devinfo_data.cbSize = sizeof(SP_DEVINFO_DATA); + device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + + /* Get information for all the devices belonging to the HID class. */ + device_info_set = SetupDiGetClassDevsA(&InterfaceClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + + /* Iterate over each device in the HID class, looking for the right one. */ + + for (;;) { + HANDLE write_handle = INVALID_HANDLE_VALUE; + DWORD required_size = 0; + HIDD_ATTRIBUTES attrib; + + res = SetupDiEnumDeviceInterfaces(device_info_set, + NULL, + &InterfaceClassGuid, + device_index, + &device_interface_data); + + if (!res) { + /* A return of FALSE from this function means that + there are no more devices. */ + break; + } + + /* Call with 0-sized detail size, and let the function + tell us how long the detail struct needs to be. The + size is put in &required_size. */ + res = SetupDiGetDeviceInterfaceDetailA(device_info_set, + &device_interface_data, + NULL, + 0, + &required_size, + NULL); + + /* Allocate a long enough structure for device_interface_detail_data. */ + device_interface_detail_data = (SP_DEVICE_INTERFACE_DETAIL_DATA_A*) malloc(required_size); + device_interface_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A); + + /* Get the detailed data for this device. The detail data gives us + the device path for this device, which is then passed into + CreateFile() to get a handle to the device. */ + res = SetupDiGetDeviceInterfaceDetailA(device_info_set, + &device_interface_data, + device_interface_detail_data, + required_size, + NULL, + NULL); + + if (!res) { + /* register_error(dev, "Unable to call SetupDiGetDeviceInterfaceDetail"); + Continue to the next device. */ + goto cont; + } + + /* Make sure this device is of Setup Class "HIDClass" and has a + driver bound to it. */ + for (i = 0; ; i++) { + char driver_name[256]; + + /* Populate devinfo_data. This function will return failure + when there are no more interfaces left. */ + res = SetupDiEnumDeviceInfo(device_info_set, i, &devinfo_data); + if (!res) + goto cont; + + res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data, + SPDRP_CLASS, NULL, (PBYTE)driver_name, sizeof(driver_name), NULL); + if (!res) + goto cont; + + if ((strcmp(driver_name, "HIDClass") == 0) || + (strcmp(driver_name, "Mouse") == 0) || + (strcmp(driver_name, "Keyboard") == 0)) { + /* See if there's a driver bound. */ + res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data, + SPDRP_DRIVER, NULL, (PBYTE)driver_name, sizeof(driver_name), NULL); + if (res) + break; + } + } + + //wprintf(L"HandleName: %s\n", device_interface_detail_data->DevicePath); + + /* Open a handle to the device */ + write_handle = open_device(device_interface_detail_data->DevicePath, FALSE); + + /* Check validity of write_handle. */ + if (write_handle == INVALID_HANDLE_VALUE) { + /* Unable to open the device. */ + //register_error(dev, "CreateFile"); + goto cont_close; + } + + + /* Get the Vendor ID and Product ID for this device. */ + attrib.Size = sizeof(HIDD_ATTRIBUTES); + HidD_GetAttributes(write_handle, &attrib); + //wprintf(L"Product/Vendor: %x %x\n", attrib.ProductID, attrib.VendorID); + + /* Check the VID/PID to see if we should add this + device to the enumeration list. */ + if ((vendor_id == 0x0 || attrib.VendorID == vendor_id) && + (product_id == 0x0 || attrib.ProductID == product_id)) { + + #define WSTR_LEN 512 + const char *str; + struct hid_device_info *tmp; + PHIDP_PREPARSED_DATA pp_data = NULL; + HIDP_CAPS caps; + BOOLEAN res; + NTSTATUS nt_res; + wchar_t wstr[WSTR_LEN]; /* TODO: Determine Size */ + size_t len; + + /* VID/PID match. Create the record. */ + tmp = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info)); + if (cur_dev) { + cur_dev->next = tmp; + } + else { + root = tmp; + } + cur_dev = tmp; + + /* Get the Usage Page and Usage for this device. */ + res = HidD_GetPreparsedData(write_handle, &pp_data); + if (res) { + nt_res = HidP_GetCaps(pp_data, &caps); + if (nt_res == HIDP_STATUS_SUCCESS) { + cur_dev->usage_page = caps.UsagePage; + cur_dev->usage = caps.Usage; + } + + HidD_FreePreparsedData(pp_data); + } + + /* Fill out the record */ + cur_dev->next = NULL; + str = device_interface_detail_data->DevicePath; + if (str) { + len = strlen(str); + cur_dev->path = (char*) calloc(len+1, sizeof(char)); + strncpy(cur_dev->path, str, len+1); + cur_dev->path[len] = '\0'; + } + else + cur_dev->path = NULL; + + /* Serial Number */ + res = HidD_GetSerialNumberString(write_handle, wstr, sizeof(wstr)); + wstr[WSTR_LEN-1] = 0x0000; + if (res) { + cur_dev->serial_number = _wcsdup(wstr); + } + + /* Manufacturer String */ + res = HidD_GetManufacturerString(write_handle, wstr, sizeof(wstr)); + wstr[WSTR_LEN-1] = 0x0000; + if (res) { + cur_dev->manufacturer_string = _wcsdup(wstr); + } + + /* Product String */ + res = HidD_GetProductString(write_handle, wstr, sizeof(wstr)); + wstr[WSTR_LEN-1] = 0x0000; + if (res) { + cur_dev->product_string = _wcsdup(wstr); + } + + /* VID/PID */ + cur_dev->vendor_id = attrib.VendorID; + cur_dev->product_id = attrib.ProductID; + + /* Release Number */ + cur_dev->release_number = attrib.VersionNumber; + + /* Interface Number. It can sometimes be parsed out of the path + on Windows if a device has multiple interfaces. See + http://msdn.microsoft.com/en-us/windows/hardware/gg487473 or + search for "Hardware IDs for HID Devices" at MSDN. If it's not + in the path, it's set to -1. */ + cur_dev->interface_number = -1; + if (cur_dev->path) { + char *interface_component = strstr(cur_dev->path, "&mi_"); + if (interface_component) { + char *hex_str = interface_component + 4; + char *endptr = NULL; + cur_dev->interface_number = strtol(hex_str, &endptr, 16); + if (endptr == hex_str) { + /* The parsing failed. Set interface_number to -1. */ + cur_dev->interface_number = -1; + } + } + } + } + +cont_close: + CloseHandle(write_handle); +cont: + /* We no longer need the detail data. It can be freed */ + free(device_interface_detail_data); + + device_index++; + + } + + /* Close the device information handle. */ + SetupDiDestroyDeviceInfoList(device_info_set); + + return root; + +} + +void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs) +{ + /* TODO: Merge this with the Linux version. This function is platform-independent. */ + struct hid_device_info *d = devs; + while (d) { + struct hid_device_info *next = d->next; + free(d->path); + free(d->serial_number); + free(d->manufacturer_string); + free(d->product_string); + free(d); + d = next; + } +} + + +HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number) +{ + /* TODO: Merge this functions with the Linux version. This function should be platform independent. */ + struct hid_device_info *devs, *cur_dev; + const char *path_to_open = NULL; + hid_device *handle = NULL; + + devs = hid_enumerate(vendor_id, product_id); + cur_dev = devs; + while (cur_dev) { + if (cur_dev->vendor_id == vendor_id && + cur_dev->product_id == product_id) { + if (serial_number) { + if (wcscmp(serial_number, cur_dev->serial_number) == 0) { + path_to_open = cur_dev->path; + break; + } + } + else { + path_to_open = cur_dev->path; + break; + } + } + cur_dev = cur_dev->next; + } + + if (path_to_open) { + /* Open the device */ + handle = hid_open_path(path_to_open); + } + + hid_free_enumeration(devs); + + return handle; +} + +HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path) +{ + hid_device *dev; + HIDP_CAPS caps; + PHIDP_PREPARSED_DATA pp_data = NULL; + BOOLEAN res; + NTSTATUS nt_res; + + if (hid_init() < 0) { + return NULL; + } + + dev = new_hid_device(); + + /* Open a handle to the device */ + dev->device_handle = open_device(path, TRUE); + + /* Check validity of write_handle. */ + if (dev->device_handle == INVALID_HANDLE_VALUE) { + /* System devices, such as keyboards and mice, cannot be opened in + read-write mode, because the system takes exclusive control over + them. This is to prevent keyloggers. However, feature reports + can still be sent and received. Retry opening the device, but + without read/write access. */ + dev->device_handle = open_device(path, FALSE); + + /* Check the validity of the limited device_handle. */ + if (dev->device_handle == INVALID_HANDLE_VALUE) { + /* Unable to open the device, even without read-write mode. */ + register_error(dev, "CreateFile"); + goto err; + } + } + + /* Set the Input Report buffer size to 64 reports. */ + res = HidD_SetNumInputBuffers(dev->device_handle, 64); + if (!res) { + register_error(dev, "HidD_SetNumInputBuffers"); + goto err; + } + + /* Get the Input Report length for the device. */ + res = HidD_GetPreparsedData(dev->device_handle, &pp_data); + if (!res) { + register_error(dev, "HidD_GetPreparsedData"); + goto err; + } + nt_res = HidP_GetCaps(pp_data, &caps); + if (nt_res != HIDP_STATUS_SUCCESS) { + register_error(dev, "HidP_GetCaps"); + goto err_pp_data; + } + dev->output_report_length = caps.OutputReportByteLength; + dev->input_report_length = caps.InputReportByteLength; + HidD_FreePreparsedData(pp_data); + + dev->read_buf = (char*) malloc(dev->input_report_length); + + return dev; + +err_pp_data: + HidD_FreePreparsedData(pp_data); +err: + free_hid_device(dev); + return NULL; +} + +int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length) +{ + DWORD bytes_written; + BOOL res; + + OVERLAPPED ol; + unsigned char *buf; + memset(&ol, 0, sizeof(ol)); + + /* Make sure the right number of bytes are passed to WriteFile. Windows + expects the number of bytes which are in the _longest_ report (plus + one for the report number) bytes even if the data is a report + which is shorter than that. Windows gives us this value in + caps.OutputReportByteLength. If a user passes in fewer bytes than this, + create a temporary buffer which is the proper size. */ + if (length >= dev->output_report_length) { + /* The user passed the right number of bytes. Use the buffer as-is. */ + buf = (unsigned char *) data; + } else { + /* Create a temporary buffer and copy the user's data + into it, padding the rest with zeros. */ + buf = (unsigned char *) malloc(dev->output_report_length); + memcpy(buf, data, length); + memset(buf + length, 0, dev->output_report_length - length); + length = dev->output_report_length; + } + + res = WriteFile(dev->device_handle, buf, length, NULL, &ol); + + if (!res) { + if (GetLastError() != ERROR_IO_PENDING) { + /* WriteFile() failed. Return error. */ + register_error(dev, "WriteFile"); + bytes_written = -1; + goto end_of_function; + } + } + + /* Wait here until the write is done. This makes + hid_write() synchronous. */ + res = GetOverlappedResult(dev->device_handle, &ol, &bytes_written, TRUE/*wait*/); + if (!res) { + /* The Write operation failed. */ + register_error(dev, "WriteFile"); + bytes_written = -1; + goto end_of_function; + } + +end_of_function: + if (buf != data) + free(buf); + + return bytes_written; +} + + +int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) +{ + DWORD bytes_read = 0; + size_t copy_len = 0; + BOOL res; + + /* Copy the handle for convenience. */ + HANDLE ev = dev->ol.hEvent; + + if (!dev->read_pending) { + /* Start an Overlapped I/O read. */ + dev->read_pending = TRUE; + memset(dev->read_buf, 0, dev->input_report_length); + ResetEvent(ev); + res = ReadFile(dev->device_handle, dev->read_buf, dev->input_report_length, &bytes_read, &dev->ol); + + if (!res) { + if (GetLastError() != ERROR_IO_PENDING) { + /* ReadFile() has failed. + Clean up and return error. */ + CancelIo(dev->device_handle); + dev->read_pending = FALSE; + goto end_of_function; + } + } + } + + if (milliseconds >= 0) { + /* See if there is any data yet. */ + res = WaitForSingleObject(ev, milliseconds); + if (res != WAIT_OBJECT_0) { + /* There was no data this time. Return zero bytes available, + but leave the Overlapped I/O running. */ + return 0; + } + } + + /* Either WaitForSingleObject() told us that ReadFile has completed, or + we are in non-blocking mode. Get the number of bytes read. The actual + data has been copied to the data[] array which was passed to ReadFile(). */ + res = GetOverlappedResult(dev->device_handle, &dev->ol, &bytes_read, TRUE/*wait*/); + + /* Set pending back to false, even if GetOverlappedResult() returned error. */ + dev->read_pending = FALSE; + + if (res && bytes_read > 0) { + if (dev->read_buf[0] == 0x0) { + /* If report numbers aren't being used, but Windows sticks a report + number (0x0) on the beginning of the report anyway. To make this + work like the other platforms, and to make it work more like the + HID spec, we'll skip over this byte. */ + bytes_read--; + copy_len = length > bytes_read ? bytes_read : length; + memcpy(data, dev->read_buf+1, copy_len); + } + else { + /* Copy the whole buffer, report number and all. */ + copy_len = length > bytes_read ? bytes_read : length; + memcpy(data, dev->read_buf, copy_len); + } + } + +end_of_function: + if (!res) { + register_error(dev, "GetOverlappedResult"); + return -1; + } + + return copy_len; +} + +int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length) +{ + return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0); +} + +int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock) +{ + dev->blocking = !nonblock; + return 0; /* Success */ +} + +int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length) +{ + BOOL res = HidD_SetFeature(dev->device_handle, (PVOID)data, length); + if (!res) { + register_error(dev, "HidD_SetFeature"); + return -1; + } + + return length; +} + + +int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length) +{ + BOOL res; +#if 0 + res = HidD_GetFeature(dev->device_handle, data, length); + if (!res) { + register_error(dev, "HidD_GetFeature"); + return -1; + } + return 0; /* HidD_GetFeature() doesn't give us an actual length, unfortunately */ +#else + DWORD bytes_returned; + + OVERLAPPED ol; + memset(&ol, 0, sizeof(ol)); + + res = DeviceIoControl(dev->device_handle, + IOCTL_HID_GET_FEATURE, + data, length, + data, length, + &bytes_returned, &ol); + + if (!res) { + if (GetLastError() != ERROR_IO_PENDING) { + /* DeviceIoControl() failed. Return error. */ + register_error(dev, "Send Feature Report DeviceIoControl"); + return -1; + } + } + + /* Wait here until the write is done. This makes + hid_get_feature_report() synchronous. */ + res = GetOverlappedResult(dev->device_handle, &ol, &bytes_returned, TRUE/*wait*/); + if (!res) { + /* The operation failed. */ + register_error(dev, "Send Feature Report GetOverLappedResult"); + return -1; + } + + /* bytes_returned does not include the first byte which contains the + report ID. The data buffer actually contains one more byte than + bytes_returned. */ + bytes_returned++; + + return bytes_returned; +#endif +} + +void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev) +{ + if (!dev) + return; + CancelIo(dev->device_handle); + free_hid_device(dev); +} + +int HID_API_EXPORT_CALL HID_API_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + BOOL res; + + res = HidD_GetManufacturerString(dev->device_handle, string, sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)); + if (!res) { + register_error(dev, "HidD_GetManufacturerString"); + return -1; + } + + return 0; +} + +int HID_API_EXPORT_CALL HID_API_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + BOOL res; + + res = HidD_GetProductString(dev->device_handle, string, sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)); + if (!res) { + register_error(dev, "HidD_GetProductString"); + return -1; + } + + return 0; +} + +int HID_API_EXPORT_CALL HID_API_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + BOOL res; + + res = HidD_GetSerialNumberString(dev->device_handle, string, sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)); + if (!res) { + register_error(dev, "HidD_GetSerialNumberString"); + return -1; + } + + return 0; +} + +int HID_API_EXPORT_CALL HID_API_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen) +{ + BOOL res; + + res = HidD_GetIndexedString(dev->device_handle, string_index, string, sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)); + if (!res) { + register_error(dev, "HidD_GetIndexedString"); + return -1; + } + + return 0; +} + + +HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev) +{ + return (wchar_t*)dev->last_error_str; +} + + +/*#define PICPGM*/ +/*#define S11*/ +#define P32 +#ifdef S11 + unsigned short VendorID = 0xa0a0; + unsigned short ProductID = 0x0001; +#endif + +#ifdef P32 + unsigned short VendorID = 0x04d8; + unsigned short ProductID = 0x3f; +#endif + + +#ifdef PICPGM + unsigned short VendorID = 0x04d8; + unsigned short ProductID = 0x0033; +#endif + + +#if 0 +int __cdecl main(int argc, char* argv[]) +{ + int res; + unsigned char buf[65]; + + UNREFERENCED_PARAMETER(argc); + UNREFERENCED_PARAMETER(argv); + + /* Set up the command buffer. */ + memset(buf,0x00,sizeof(buf)); + buf[0] = 0; + buf[1] = 0x81; + + + /* Open the device. */ + int handle = open(VendorID, ProductID, L"12345"); + if (handle < 0) + printf("unable to open device\n"); + + + /* Toggle LED (cmd 0x80) */ + buf[1] = 0x80; + res = write(handle, buf, 65); + if (res < 0) + printf("Unable to write()\n"); + + /* Request state (cmd 0x81) */ + buf[1] = 0x81; + write(handle, buf, 65); + if (res < 0) + printf("Unable to write() (2)\n"); + + /* Read requested state */ + read(handle, buf, 65); + if (res < 0) + printf("Unable to read()\n"); + + /* Print out the returned buffer. */ + for (int i = 0; i < 4; i++) + printf("buf[%d]: %d\n", i, buf[i]); + + return 0; +} +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 51d092094..da982c49a 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -32,4 +32,13 @@ #define ENABLE_NONCUSTOM_DATA_VIEW_RENDERING (0 && ENABLE_1_42_0_ALPHA1) +//==================== +// 2.0.0.alpha1 techs +//==================== +#define ENABLE_2_0_0_ALPHA1 1 + +// Enabled 3Dconnexion devices +#define ENABLE_3DCONNEXION_DEVICES (1 && ENABLE_2_0_0_ALPHA1) + + #endif // _technologies_h_ diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 17b76e629..6218f3837 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -136,6 +136,8 @@ set(SLIC3R_GUI_SOURCES GUI/ProgressStatusBar.cpp GUI/PrintHostDialogs.cpp GUI/PrintHostDialogs.hpp + GUI/Mouse3DController.cpp + GUI/Mouse3DController.hpp Utils/Http.cpp Utils/Http.hpp Utils/FixModelByWin10.cpp @@ -167,7 +169,7 @@ add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES}) encoding_check(libslic3r_gui) -target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui ${GLEW_LIBRARIES}) +target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui ${GLEW_LIBRARIES} ${HIDAPI_LIBRARIES}) if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY) add_precompiled_header(libslic3r_gui pchheader.hpp FORCEINCLUDE) endif () diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 715d9f806..2960d08c3 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -22,6 +22,9 @@ #include "GUI_App.hpp" #include "GUI_ObjectList.hpp" #include "GUI_ObjectManipulation.hpp" +#if ENABLE_3DCONNEXION_DEVICES +#include "Mouse3DController.hpp" +#endif // ENABLE_3DCONNEXION_DEVICES #include "I18N.hpp" #if ENABLE_RETINA_GL @@ -2305,14 +2308,28 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt) m_dirty |= m_main_toolbar.update_items_state(); m_dirty |= m_undoredo_toolbar.update_items_state(); m_dirty |= m_view_toolbar.update_items_state(); +#if ENABLE_3DCONNEXION_DEVICES + bool mouse3d_controller_applied = wxGetApp().plater()->get_mouse3d_controller().apply(); + m_dirty |= mouse3d_controller_applied; +#endif // ENABLE_3DCONNEXION_DEVICES if (!m_dirty) return; _refresh_if_shown_on_screen(); +#if ENABLE_3DCONNEXION_DEVICES + if (m_keep_dirty || wxGetApp().plater()->get_mouse3d_controller().is_device_connected()) + { + m_dirty = true; + evt.RequestMore(); + } + else + m_dirty = false; +#else if (m_keep_dirty) m_dirty = true; +#endif // ENABLE_3DCONNEXION_DEVICES } void GLCanvas3D::on_char(wxKeyEvent& evt) @@ -2531,6 +2548,13 @@ void GLCanvas3D::on_key(wxKeyEvent& evt) void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt) { +#if ENABLE_3DCONNEXION_DEVICES + // try to filter out events coming from mouse 3d controller + const Mouse3DController& controller = wxGetApp().plater()->get_mouse3d_controller(); + if (controller.has_rotation() || controller.has_translation()) + return; +#endif // ENABLE_3DCONNEXION_DEVICES + if (!m_initialized) return; @@ -3815,7 +3839,9 @@ void GLCanvas3D::_resize(unsigned int w, unsigned int h) // updates camera m_camera.apply_viewport(0, 0, w, h); +#if !ENABLE_3DCONNEXION_DEVICES m_dirty = false; +#endif // !ENABLE_3DCONNEXION_DEVICES } BoundingBoxf3 GLCanvas3D::_max_bounding_box(bool include_gizmos, bool include_bed_model) const diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index fb767360c..cce6b0f2b 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -486,6 +486,9 @@ public: void set_color_by(const std::string& value); const Camera& get_camera() const { return m_camera; } +#if ENABLE_3DCONNEXION_DEVICES + Camera& get_camera() { return m_camera; } +#endif // ENABLE_3DCONNEXION_DEVICES BoundingBoxf3 volumes_bounding_box() const; BoundingBoxf3 scene_bounding_box() const; diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index dfe3a9cf9..ae0413937 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -24,6 +24,9 @@ #include "PrintHostDialogs.hpp" #include "wxExtensions.hpp" #include "GUI_ObjectList.hpp" +#if ENABLE_3DCONNEXION_DEVICES +#include "Mouse3DController.hpp" +#endif // ENABLE_3DCONNEXION_DEVICES #include "I18N.hpp" #include @@ -108,6 +111,10 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S } if(m_plater) m_plater->stop_jobs(); +#if ENABLE_3DCONNEXION_DEVICES + if (m_plater != nullptr) + m_plater->get_mouse3d_controller().set_canvas(nullptr); +#endif // ENABLE_3DCONNEXION_DEVICES // Weird things happen as the Paint messages are floating around the windows being destructed. // Avoid the Paint messages by hiding the main window. diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp new file mode 100644 index 000000000..43faf852b --- /dev/null +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -0,0 +1,332 @@ +#include "libslic3r/libslic3r.h" +#include "Mouse3DController.hpp" + +#if ENABLE_3DCONNEXION_DEVICES + +#include "GLCanvas3D.hpp" +#include "GUI_App.hpp" +#include "PresetBundle.hpp" + +#include + +static const std::vector _3DCONNEXION_VENDORS = +{ + 0x046d, // LOGITECH = 1133 // Logitech (3Dconnexion is made by Logitech) + 0x256F // 3DCONNECTION = 9583 // 3Dconnexion +}; + +static const std::vector _3DCONNEXION_DEVICES = +{ + 0xC623, // TRAVELER = 50723 + 0xC626, // NAVIGATOR = 50726 + 0xc628, // NAVIGATOR_FOR_NOTEBOOKS = 50728 + 0xc627, // SPACEEXPLORER = 50727 + 0xC603, // SPACEMOUSE = 50691 + 0xC62B, // SPACEMOUSEPRO = 50731 + 0xc621, // SPACEBALL5000 = 50721 + 0xc625, // SPACEPILOT = 50725 + 0xc629 // SPACEPILOTPRO = 50729 +}; + +static const unsigned int _3DCONNEXION_BUTTONS_COUNT = 2; + +namespace Slic3r { +namespace GUI { + +const double Mouse3DController::State::DefaultTranslationScale = 2.5; +const float Mouse3DController::State::DefaultRotationScale = 1.0; + +Mouse3DController::State::State() + : m_translation(Vec3d::Zero()) + , m_rotation(Vec3f::Zero()) + , m_buttons(_3DCONNEXION_BUTTONS_COUNT, false) + , m_translation_scale(DefaultTranslationScale) + , m_rotation_scale(DefaultRotationScale) +{ +} + +void Mouse3DController::State::set_translation(const Vec3d& translation) +{ + std::lock_guard lock(m_mutex); + m_translation = translation; +} + +void Mouse3DController::State::set_rotation(const Vec3f& rotation) +{ + std::lock_guard lock(m_mutex); + m_rotation = rotation; +} + +void Mouse3DController::State::set_button(unsigned int id) +{ + std::lock_guard lock(m_mutex); + if (id < _3DCONNEXION_BUTTONS_COUNT) + m_buttons[id] = true; +} + +bool Mouse3DController::State::has_translation() const +{ + std::lock_guard lock(m_mutex); + return !m_translation.isApprox(Vec3d::Zero()); +} +bool Mouse3DController::State::has_rotation() const +{ + std::lock_guard lock(m_mutex); + return !m_rotation.isApprox(Vec3f::Zero()); +} + +bool Mouse3DController::State::has_any_button() const +{ + std::lock_guard lock(m_mutex); + for (int i = 0; i < _3DCONNEXION_BUTTONS_COUNT; ++i) + { + if (m_buttons[i]) + return true; + } + return false; +} + +bool Mouse3DController::State::apply(GLCanvas3D& canvas) +{ + if (!wxGetApp().IsActive()) + return false; + + bool ret = false; + Camera& camera = canvas.get_camera(); + + if (has_translation()) + { + camera.set_target(camera.get_target() + m_translation_scale * (m_translation(0) * camera.get_dir_right() + m_translation(1) * camera.get_dir_forward() + m_translation(2) * camera.get_dir_up())); + m_translation = Vec3d::Zero(); + ret = true; + } + + if (has_rotation()) + { + float theta = m_rotation_scale * m_rotation(0); + float phi = m_rotation_scale * m_rotation(2); + float sign = camera.inverted_phi ? -1.0f : 1.0f; + camera.phi += sign * phi; + camera.set_theta(camera.get_theta() + theta, wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA); + m_rotation = Vec3f::Zero(); + ret = true; + } + + if (has_any_button()) + { + if (m_buttons[0]) + canvas.set_camera_zoom(1.0); + else if (m_buttons[1]) + canvas.set_camera_zoom(-1.0); + + m_buttons = std::vector(_3DCONNEXION_BUTTONS_COUNT, false); + ret = true; + } + + return ret; +} + +Mouse3DController::Mouse3DController() + : m_initialized(false) + , m_canvas(nullptr) + , m_device(nullptr) + , m_running(false) +{ +} + +void Mouse3DController::init() +{ + if (m_initialized) + return; + + // Initialize the hidapi library + int res = hid_init(); + if (res != 0) + return; + + m_initialized = true; + + connect_device(); + start(); +} + +void Mouse3DController::shutdown() +{ + if (!m_initialized) + return; + + stop(); + + if (m_thread.joinable()) + m_thread.join(); + + disconnect_device(); + + // Finalize the hidapi library + hid_exit(); + m_initialized = false; +} + +void Mouse3DController::connect_device() +{ + if (m_device != nullptr) + return; + + // Enumerates devices + hid_device_info* devices = hid_enumerate(0, 0); + if (devices == nullptr) + return; + + // Searches for 1st connected 3Dconnexion device + unsigned short vendor_id = 0; + unsigned short product_id = 0; + + hid_device_info* current = devices; + while (current != nullptr) + { + for (size_t i = 0; i < _3DCONNEXION_VENDORS.size(); ++i) + { + if (_3DCONNEXION_VENDORS[i] == current->vendor_id) + { + vendor_id = current->vendor_id; + break; + } + } + + if (vendor_id != 0) + { + for (size_t i = 0; i < _3DCONNEXION_DEVICES.size(); ++i) + { + if (_3DCONNEXION_DEVICES[i] == current->product_id) + { + product_id = current->product_id; + break; + } + } + + if (product_id == 0) + vendor_id = 0; + } + + if (vendor_id != 0) + break; + + current = current->next; + } + + // Free enumerated devices + hid_free_enumeration(devices); + + if (vendor_id == 0) + return; + + // Open the 3Dconnexion device using the VID, PID + m_device = hid_open(vendor_id, product_id, nullptr); +} + +void Mouse3DController::disconnect_device() +{ + if (m_device == nullptr) + return; + + // Close the 3Dconnexion device + hid_close(m_device); + m_device = nullptr; +} + +void Mouse3DController::start() +{ + if ((m_device == nullptr) || m_running) + return; + + m_thread = std::thread(&Mouse3DController::run, this); +} + +void Mouse3DController::run() +{ + m_running = true; + while (m_running) + { + collect_input(); + } +} + +double convert_input(int first, unsigned char val) +{ + int ret = 0; + + switch (val) + { + case 0: { ret = first; break; } + case 1: { ret = first + 255; break; } + case 254: { ret = -511 + first; break; } + case 255: { ret = -255 + first; break; } + default: { break; } + } + + return (double)ret / 349.0; +} + +void Mouse3DController::collect_input() +{ + // Read data from device + enum EDataType + { + Translation = 1, + Rotation, + Button + }; + + unsigned char retrieved_data[8] = { 0 }; + int res = hid_read_timeout(m_device, retrieved_data, sizeof(retrieved_data), 100); + if (res < 0) + { + // An error occourred (device detached from pc ?) + stop(); + return; + } + + if (res > 0) + { + switch (retrieved_data[0]) + { + case Translation: + { + Vec3d translation(-convert_input((int)retrieved_data[1], retrieved_data[2]), + convert_input((int)retrieved_data[3], retrieved_data[4]), + convert_input((int)retrieved_data[5], retrieved_data[6])); + if (!translation.isApprox(Vec3d::Zero())) + m_state.set_translation(translation); + + break; + } + case Rotation: + { + Vec3f rotation(-(float)convert_input((int)retrieved_data[1], retrieved_data[2]), + (float)convert_input((int)retrieved_data[3], retrieved_data[4]), + -(float)convert_input((int)retrieved_data[5], retrieved_data[6])); + if (!rotation.isApprox(Vec3f::Zero())) + m_state.set_rotation(rotation); + + break; + } + case Button: + { + for (unsigned int i = 0; i < _3DCONNEXION_BUTTONS_COUNT; ++i) + { + if (retrieved_data[1] & (0x1 << i)) + m_state.set_button(i); + } + + break; + } + default: + break; + } + } +} + +} // namespace GUI +} // namespace Slic3r + +#endif // ENABLE_3DCONNEXION_DEVICES diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp new file mode 100644 index 000000000..b62a00b6f --- /dev/null +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -0,0 +1,96 @@ +#ifndef slic3r_Mouse3DController_hpp_ +#define slic3r_Mouse3DController_hpp_ + +#if ENABLE_3DCONNEXION_DEVICES + +#include "hidapi/hidapi.h" +#include +#include +#include + +namespace Slic3r { +namespace GUI { + +class GLCanvas3D; + +class Mouse3DController +{ + class State + { + static const double DefaultTranslationScale; + static const float DefaultRotationScale; + + mutable std::mutex m_mutex; + + Vec3d m_translation; + Vec3f m_rotation; + std::vector m_buttons; + + double m_translation_scale; + float m_rotation_scale; + + public: + State(); + + void set_translation(const Vec3d& translation); + void set_rotation(const Vec3f& rotation); + void set_button(unsigned int id); + + bool has_translation() const; + bool has_rotation() const; + bool has_any_button() const; + + // return true if any change to the camera took place + bool apply(GLCanvas3D& canvas); + }; + + bool m_initialized; + State m_state; + std::thread m_thread; + GLCanvas3D* m_canvas; + std::mutex m_mutex; + hid_device* m_device; + bool m_running; + +public: + Mouse3DController(); + + void init(); + void shutdown(); + + bool is_device_connected() const { return m_device != nullptr; } + bool is_running() const { return m_running; } + + void set_canvas(GLCanvas3D* canvas) + { + std::lock_guard lock(m_mutex); + m_canvas = canvas; + } + + bool has_translation() const { return m_state.has_translation(); } + bool has_rotation() const { return m_state.has_rotation(); } + bool has_any_button() const { return m_state.has_any_button(); } + + bool apply() + { + std::lock_guard lock(m_mutex); + return (m_canvas != nullptr) ? m_state.apply(*m_canvas) : false; + } + +private: + void connect_device(); + void disconnect_device(); + void start(); + void stop() { m_running = false; } + + void run(); + void collect_input(); +}; + +} // namespace GUI +} // namespace Slic3r + +#endif // ENABLE_3DCONNEXION_DEVICES + +#endif // slic3r_Mouse3DController_hpp_ + diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index c7f1f4c5c..b721ee8fd 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -61,6 +61,9 @@ #include "GUI_Preview.hpp" #include "3DBed.hpp" #include "Camera.hpp" +#if ENABLE_3DCONNEXION_DEVICES +#include "Mouse3DController.hpp" +#endif // ENABLE_3DCONNEXION_DEVICES #include "Tab.hpp" #include "PresetBundle.hpp" #include "BackgroundSlicingProcess.hpp" @@ -1367,6 +1370,9 @@ struct Plater::priv Sidebar *sidebar; Bed3D bed; Camera camera; +#if ENABLE_3DCONNEXION_DEVICES + Mouse3DController mouse3d_controller; +#endif // ENABLE_3DCONNEXION_DEVICES View3D* view3D; GLToolbar view_toolbar; Preview *preview; @@ -2094,12 +2100,20 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) // updates camera type from .ini file camera.set_type(get_config("use_perspective_camera")); +#if ENABLE_3DCONNEXION_DEVICES + mouse3d_controller.init(); +#endif // ENABLE_3DCONNEXION_DEVICES + // Initialize the Undo / Redo stack with a first snapshot. this->take_snapshot(_(L("New Project"))); } Plater::priv::~priv() { +#if ENABLE_3DCONNEXION_DEVICES + mouse3d_controller.shutdown(); +#endif // ENABLE_3DCONNEXION_DEVICES + if (config != nullptr) delete config; } @@ -3248,6 +3262,11 @@ void Plater::priv::set_current_panel(wxPanel* panel) } else view3D->reload_scene(true); } + +#if ENABLE_3DCONNEXION_DEVICES + mouse3d_controller.set_canvas(view3D->get_canvas3d()); +#endif // ENABLE_3DCONNEXION_DEVICES + // sets the canvas as dirty to force a render at the 1st idle event (wxWidgets IsShownOnScreen() is buggy and cannot be used reliably) view3D->set_as_dirty(); view_toolbar.select_item("3D"); @@ -3262,6 +3281,11 @@ void Plater::priv::set_current_panel(wxPanel* panel) this->q->reslice(); // keeps current gcode preview, if any preview->reload_print(true); + +#if ENABLE_3DCONNEXION_DEVICES + mouse3d_controller.set_canvas(preview->get_canvas3d()); +#endif // ENABLE_3DCONNEXION_DEVICES + preview->set_canvas_as_dirty(); view_toolbar.select_item("Preview"); } @@ -5020,6 +5044,18 @@ const Camera& Plater::get_camera() const return p->camera; } +#if ENABLE_3DCONNEXION_DEVICES +const Mouse3DController& Plater::get_mouse3d_controller() const +{ + return p->mouse3d_controller; +} + +Mouse3DController& Plater::get_mouse3d_controller() +{ + return p->mouse3d_controller; +} +#endif // ENABLE_3DCONNEXION_DEVICES + bool Plater::can_delete() const { return p->can_delete(); } bool Plater::can_delete_all() const { return p->can_delete_all(); } bool Plater::can_increase_instances() const { return p->can_increase_instances(); } diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 26dcb5ac3..c217c8217 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -41,6 +41,7 @@ class ObjectSettings; class ObjectLayers; class ObjectList; class GLCanvas3D; +class Mouse3DController; using t_optgroups = std::vector >; @@ -251,6 +252,10 @@ public: void msw_rescale(); const Camera& get_camera() const; +#if ENABLE_3DCONNEXION_DEVICES + const Mouse3DController& get_mouse3d_controller() const; + Mouse3DController& get_mouse3d_controller(); +#endif // ENABLE_3DCONNEXION_DEVICES // ROII wrapper for suppressing the Undo / Redo snapshot to be taken. class SuppressSnapshots From 33bfc925c2a65861fbc77f97adeb6bfe59e793d2 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 27 Sep 2019 15:02:38 +0200 Subject: [PATCH 02/67] ENABLE_3DCONNEXION_DEVICES -> try a different hid.c for hidapi library on Linux --- src/hidapi/linux/hid.c | 1723 ++++++++++++++++++++++++++++------------ 1 file changed, 1220 insertions(+), 503 deletions(-) diff --git a/src/hidapi/linux/hid.c b/src/hidapi/linux/hid.c index 56dac0fab..8cd5e3dca 100644 --- a/src/hidapi/linux/hid.c +++ b/src/hidapi/linux/hid.c @@ -6,7 +6,9 @@ Signal 11 Software 8/22/2009 - Linux Version - 6/2/2009 + Linux Version - 6/2/2010 + Libusb Version - 8/13/2010 + FreeBSD Version - 11/1/2011 Copyright 2009, All Rights Reserved. @@ -21,10 +23,13 @@ http://github.com/signal11/hidapi . ********************************************************/ +#define _GNU_SOURCE /* needed for wcsdup() before glibc 2.10 */ + /* C */ #include #include #include +#include #include #include @@ -35,124 +40,219 @@ #include #include #include -#include +#include +#include -/* Linux */ -#include -#include -#include -#include +/* GNU / LibUSB */ +#include +#ifndef __ANDROID__ +#include +#endif #include "hidapi.h" -/* Definitions from linux/hidraw.h. Since these are new, some distros - may not have header files which contain them. */ -#ifndef HIDIOCSFEATURE -#define HIDIOCSFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x06, len) -#endif -#ifndef HIDIOCGFEATURE -#define HIDIOCGFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x07, len) -#endif +#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; -/* USB HID device property names */ -const char *device_string_names[] = { - "manufacturer", - "product", - "serial", -}; - -/* Symbolic names for the properties above */ -enum device_string_id { - DEVICE_STRING_MANUFACTURER, - DEVICE_STRING_PRODUCT, - DEVICE_STRING_SERIAL, - - DEVICE_STRING_COUNT, -}; - -struct hid_device_ { - int device_handle; - int blocking; - int uses_numbered_reports; -}; - - -static __u32 kernel_version = 0; - -static __u32 detect_kernel_version(void) +static int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned int count) { - struct utsname name; - int major, minor, release; - int ret; - - uname(&name); - ret = sscanf(name.release, "%d.%d.%d", &major, &minor, &release); - if (ret == 3) { - return KERNEL_VERSION(major, minor, release); + if(count == 0) { + errno = EINVAL; + return -1; } - ret = sscanf(name.release, "%d.%d", &major, &minor); - if (ret == 2) { - return KERNEL_VERSION(major, minor, 0); + 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; - printf("Couldn't determine kernel version from version string \"%s\"\n", name.release); 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->device_handle = -1; dev->blocking = 1; - dev->uses_numbered_reports = 0; + + pthread_mutex_init(&dev->mutex, NULL); + pthread_cond_init(&dev->condition, NULL); + pthread_barrier_init(&dev->barrier, NULL, 2); return dev; } - -/* The caller must free the returned string with free(). */ -static wchar_t *utf8_to_wchar_t(const char *utf8) +static void free_hid_device(hid_device *dev) { - wchar_t *ret = NULL; + /* Clean up the thread objects */ + pthread_barrier_destroy(&dev->barrier); + pthread_cond_destroy(&dev->condition); + pthread_mutex_destroy(&dev->mutex); - if (utf8) { - size_t wlen = mbstowcs(NULL, utf8, 0); - if ((size_t) -1 == wlen) { - return wcsdup(L""); - } - ret = calloc(wlen+1, sizeof(wchar_t)); - mbstowcs(ret, utf8, wlen+1); - ret[wlen] = 0x0000; + /* 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]; } - - return ret; + 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; } -/* Get an attribute value from a udev_device and return it as a whar_t - string. The returned string must be freed with free() when done.*/ -static wchar_t *copy_udev_string(struct udev_device *dev, const char *udev_name) +/* 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) { - return utf8_to_wchar_t(udev_device_get_sysattr_value(dev, udev_name)); -} - -/* uses_numbered_reports() returns 1 if report_descriptor describes a device - which contains numbered reports. */ -static int uses_numbered_reports(__u8 *report_descriptor, __u32 size) { unsigned int i = 0; int size_code; int data_len, key_size; + int usage_found = 0, usage_page_found = 0; while (i < size) { int key = report_descriptor[i]; - - /* Check for the Report ID key */ - if (key == 0x85/*Report ID*/) { - /* This device has a Report ID, which means it uses - numbered reports. */ - return 1; - } + int key_cmd = key & 0xfc; //printf("key: %02hhx\n", key); @@ -191,379 +291,390 @@ static int uses_numbered_reports(__u8 *report_descriptor, __u32 size) { 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; } - /* Didn't find a Report ID key. Device doesn't use numbered reports. */ + 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; } -/* - * The caller is responsible for free()ing the (newly-allocated) character - * strings pointed to by serial_number_utf8 and product_name_utf8 after use. - */ -static int -parse_uevent_info(const char *uevent, int *bus_type, - unsigned short *vendor_id, unsigned short *product_id, - char **serial_number_utf8, char **product_name_utf8) + +/* 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 *tmp = strdup(uevent); - char *saveptr = NULL; - char *line; - char *key; - char *value; + char buf[512]; + int len; + wchar_t *str = NULL; - int found_id = 0; - int found_serial = 0; - int found_name = 0; +#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 - line = strtok_r(tmp, "\n", &saveptr); - while (line != NULL) { - /* line: "KEY=value" */ - key = line; - value = strchr(line, '='); - if (!value) { - goto next_line; - } - *value = '\0'; - value++; + /* 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); - if (strcmp(key, "HID_ID") == 0) { - /** - * type vendor product - * HID_ID=0003:000005AC:00008242 - **/ - int ret = sscanf(value, "%x:%hx:%hx", bus_type, vendor_id, product_id); - if (ret == 3) { - found_id = 1; - } - } else if (strcmp(key, "HID_NAME") == 0) { - /* The caller has to free the product name */ - *product_name_utf8 = strdup(value); - found_name = 1; - } else if (strcmp(key, "HID_UNIQ") == 0) { - /* The caller has to free the serial number */ - *serial_number_utf8 = strdup(value); - found_serial = 1; - } + /* Get the string from libusb. */ + len = libusb_get_string_descriptor(dev, + idx, + lang, + (unsigned char*)buf, + sizeof(buf)); + if (len < 0) + return NULL; -next_line: - line = strtok_r(NULL, "\n", &saveptr); +#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; } - free(tmp); - return (found_id && found_name && found_serial); + /* 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 int get_device_string(hid_device *dev, enum device_string_id key, wchar_t *string, size_t maxlen) +static char *make_path(libusb_device *dev, int interface_number) { - struct udev *udev; - struct udev_device *udev_dev, *parent, *hid_dev; - struct stat s; - int ret = -1; - char *serial_number_utf8 = NULL; - char *product_name_utf8 = NULL; + 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'; - /* Create the udev object */ - udev = udev_new(); - if (!udev) { - printf("Can't create udev\n"); - return -1; - } - - /* Get the dev_t (major/minor numbers) from the file handle. */ - ret = fstat(dev->device_handle, &s); - if (-1 == ret) - return ret; - /* Open a udev device from the dev_t. 'c' means character device. */ - udev_dev = udev_device_new_from_devnum(udev, 'c', s.st_rdev); - if (udev_dev) { - hid_dev = udev_device_get_parent_with_subsystem_devtype( - udev_dev, - "hid", - NULL); - if (hid_dev) { - unsigned short dev_vid; - unsigned short dev_pid; - int bus_type; - size_t retm; - - ret = parse_uevent_info( - udev_device_get_sysattr_value(hid_dev, "uevent"), - &bus_type, - &dev_vid, - &dev_pid, - &serial_number_utf8, - &product_name_utf8); - - if (bus_type == BUS_BLUETOOTH) { - switch (key) { - case DEVICE_STRING_MANUFACTURER: - wcsncpy(string, L"", maxlen); - ret = 0; - break; - case DEVICE_STRING_PRODUCT: - retm = mbstowcs(string, product_name_utf8, maxlen); - ret = (retm == (size_t)-1)? -1: 0; - break; - case DEVICE_STRING_SERIAL: - retm = mbstowcs(string, serial_number_utf8, maxlen); - ret = (retm == (size_t)-1)? -1: 0; - break; - case DEVICE_STRING_COUNT: - default: - ret = -1; - break; - } - } - else { - /* This is a USB device. Find its parent USB Device node. */ - parent = udev_device_get_parent_with_subsystem_devtype( - udev_dev, - "usb", - "usb_device"); - if (parent) { - const char *str; - const char *key_str = NULL; - - if (key >= 0 && key < DEVICE_STRING_COUNT) { - key_str = device_string_names[key]; - } else { - ret = -1; - goto end; - } - - str = udev_device_get_sysattr_value(parent, key_str); - if (str) { - /* Convert the string from UTF-8 to wchar_t */ - retm = mbstowcs(string, str, maxlen); - ret = (retm == (size_t)-1)? -1: 0; - goto end; - } - } - } - } - } - -end: - free(serial_number_utf8); - free(product_name_utf8); - - udev_device_unref(udev_dev); - /* parent and hid_dev don't need to be (and can't be) unref'd. - I'm not sure why, but they'll throw double-free() errors. */ - udev_unref(udev); - - return ret; + return strdup(str); } + int HID_API_EXPORT hid_init(void) { - const char *locale; + if (!usb_context) { + const char *locale; - /* Set the locale if it's not set. */ - locale = setlocale(LC_CTYPE, NULL); - if (!locale) - setlocale(LC_CTYPE, ""); + /* Init Libusb */ + if (libusb_init(&usb_context)) + return -1; - kernel_version = detect_kernel_version(); + /* 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) { - /* Nothing to do for this in the Linux/hidraw implementation. */ + 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) { - struct udev *udev; - struct udev_enumerate *enumerate; - struct udev_list_entry *devices, *dev_list_entry; + 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; - struct hid_device_info *prev_dev = NULL; /* previous device */ - hid_init(); - - /* Create the udev object */ - udev = udev_new(); - if (!udev) { - printf("Can't create udev\n"); + 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); + } } - /* Create a list of the devices in the 'hidraw' subsystem. */ - enumerate = udev_enumerate_new(udev); - udev_enumerate_add_match_subsystem(enumerate, "hidraw"); - udev_enumerate_scan_devices(enumerate); - devices = udev_enumerate_get_list_entry(enumerate); - /* For each item, see if it matches the vid/pid, and if so - create a udev_device record for it */ - udev_list_entry_foreach(dev_list_entry, devices) { - const char *sysfs_path; - const char *dev_path; - const char *str; - struct udev_device *raw_dev; /* The device's hidraw udev node. */ - struct udev_device *hid_dev; /* The device's HID udev node. */ - struct udev_device *usb_dev; /* The device's USB udev node. */ - struct udev_device *intf_dev; /* The device's interface (in the USB sense). */ - unsigned short dev_vid; - unsigned short dev_pid; - char *serial_number_utf8 = NULL; - char *product_name_utf8 = NULL; - int bus_type; - int result; - - /* Get the filename of the /sys entry for the device - and create a udev_device object (dev) representing it */ - sysfs_path = udev_list_entry_get_name(dev_list_entry); - raw_dev = udev_device_new_from_syspath(udev, sysfs_path); - dev_path = udev_device_get_devnode(raw_dev); - - hid_dev = udev_device_get_parent_with_subsystem_devtype( - raw_dev, - "hid", - NULL); - - if (!hid_dev) { - /* Unable to find parent hid device. */ - goto next; - } - - result = parse_uevent_info( - udev_device_get_sysattr_value(hid_dev, "uevent"), - &bus_type, - &dev_vid, - &dev_pid, - &serial_number_utf8, - &product_name_utf8); - - if (!result) { - /* parse_uevent_info() failed for at least one field. */ - goto next; - } - - if (bus_type != BUS_USB && bus_type != BUS_BLUETOOTH) { - /* We only know how to handle USB and BT devices. */ - goto next; - } - - /* Check the VID/PID against the arguments */ - if ((vendor_id == 0x0 || vendor_id == dev_vid) && - (product_id == 0x0 || product_id == dev_pid)) { - struct hid_device_info *tmp; - - /* VID/PID match. Create the record. */ - tmp = malloc(sizeof(struct hid_device_info)); - if (cur_dev) { - cur_dev->next = tmp; - } - else { - root = tmp; - } - prev_dev = cur_dev; - cur_dev = tmp; - - /* Fill out the record */ - cur_dev->next = NULL; - cur_dev->path = dev_path? strdup(dev_path): NULL; - - /* VID/PID */ - cur_dev->vendor_id = dev_vid; - cur_dev->product_id = dev_pid; - - /* Serial Number */ - cur_dev->serial_number = utf8_to_wchar_t(serial_number_utf8); - - /* Release Number */ - cur_dev->release_number = 0x0; - - /* Interface Number */ - cur_dev->interface_number = -1; - - switch (bus_type) { - case BUS_USB: - /* The device pointed to by raw_dev contains information about - the hidraw device. In order to get information about the - USB device, get the parent device with the - subsystem/devtype pair of "usb"/"usb_device". This will - be several levels up the tree, but the function will find - it. */ - usb_dev = udev_device_get_parent_with_subsystem_devtype( - raw_dev, - "usb", - "usb_device"); - - if (!usb_dev) { - /* Free this device */ - free(cur_dev->serial_number); - free(cur_dev->path); - free(cur_dev); - - /* Take it off the device list. */ - if (prev_dev) { - prev_dev->next = NULL; - cur_dev = prev_dev; - } - else { - cur_dev = root = NULL; - } - - goto next; - } - - /* Manufacturer and Product strings */ - cur_dev->manufacturer_string = copy_udev_string(usb_dev, device_string_names[DEVICE_STRING_MANUFACTURER]); - cur_dev->product_string = copy_udev_string(usb_dev, device_string_names[DEVICE_STRING_PRODUCT]); - - /* Release Number */ - str = udev_device_get_sysattr_value(usb_dev, "bcdDevice"); - cur_dev->release_number = (str)? strtol(str, NULL, 16): 0x0; - - /* Get a handle to the interface's udev node. */ - intf_dev = udev_device_get_parent_with_subsystem_devtype( - raw_dev, - "usb", - "usb_interface"); - if (intf_dev) { - str = udev_device_get_sysattr_value(intf_dev, "bInterfaceNumber"); - cur_dev->interface_number = (str)? strtol(str, NULL, 16): -1; - } - - break; - - case BUS_BLUETOOTH: - /* Manufacturer and Product strings */ - cur_dev->manufacturer_string = wcsdup(L""); - cur_dev->product_string = utf8_to_wchar_t(product_name_utf8); - - break; - - default: - /* Unknown device type - this should never happen, as we - * check for USB and Bluetooth devices above */ - break; - } - } - - next: - free(serial_number_utf8); - free(product_name_utf8); - udev_device_unref(raw_dev); - /* hid_dev, usb_dev and intf_dev don't need to be (and can't be) - unref()d. It will cause a double-free() error. I'm not - sure why. */ - } - /* Free the enumerator and udev objects. */ - udev_enumerate_unref(enumerate); - udev_unref(udev); + libusb_free_device_list(devs, 1); return root; } @@ -594,7 +705,8 @@ hid_device * hid_open(unsigned short vendor_id, unsigned short product_id, const if (cur_dev->vendor_id == vendor_id && cur_dev->product_id == product_id) { if (serial_number) { - if (wcscmp(serial_number, cur_dev->serial_number) == 0) { + if (cur_dev->serial_number && + wcscmp(serial_number, cur_dev->serial_number) == 0) { path_to_open = cur_dev->path; break; } @@ -617,49 +729,272 @@ hid_device * hid_open(unsigned short vendor_id, unsigned short product_id, const 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; - hid_init(); + 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(); - /* OPEN HERE */ - dev->device_handle = open(path, O_RDWR); + 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 (dev->device_handle > 0) { - - /* Get the report descriptor */ - int res, desc_size = 0; - struct hidraw_report_descriptor rpt_desc; - - memset(&rpt_desc, 0x0, sizeof(rpt_desc)); - - /* Get Report Descriptor Size */ - res = ioctl(dev->device_handle, HIDIOCGRDESCSIZE, &desc_size); - if (res < 0) - perror("HIDIOCGRDESCSIZE"); - - - /* Get Report Descriptor */ - rpt_desc.size = desc_size; - res = ioctl(dev->device_handle, HIDIOCGRDESC, &rpt_desc); - if (res < 0) { - perror("HIDIOCGRDESC"); - } else { - /* Determine if this device uses numbered reports. */ - dev->uses_numbered_reports = - uses_numbered_reports(rpt_desc.value, - rpt_desc.size); - } - + if (good_open) { return dev; } else { /* Unable to open any devices. */ - free(dev); + free_hid_device(dev); return NULL; } } @@ -667,95 +1002,231 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path) int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length) { - int bytes_written; + int res; + int report_number = data[0]; + int skipped_report_id = 0; - bytes_written = write(dev->device_handle, data, length); + if (report_number == 0x0) { + data++; + length--; + skipped_report_id = 1; + } - return bytes_written; + + 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; + int bytes_read = -1; - if (milliseconds >= 0) { - /* Milliseconds is either 0 (non-blocking) or > 0 (contains - a valid timeout). In both cases we want to call poll() - and wait for data to arrive. Don't rely on non-blocking - operation (O_NONBLOCK) since some kernels don't seem to - properly report device disconnection through read() when - in non-blocking mode. */ - int ret; - struct pollfd fds; +#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 - fds.fd = dev->device_handle; - fds.events = POLLIN; - fds.revents = 0; - ret = poll(&fds, 1, milliseconds); - if (ret == -1 || ret == 0) { - /* Error or timeout */ - return ret; - } - else { - /* Check for errors on the file descriptor. This will - indicate a device disconnection. */ - if (fds.revents & (POLLERR | POLLHUP | POLLNVAL)) - return -1; - } + 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; } - bytes_read = read(dev->device_handle, data, length); - if (bytes_read < 0 && (errno == EAGAIN || errno == EINPROGRESS)) + 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; - - if (bytes_read >= 0 && - kernel_version != 0 && - kernel_version < KERNEL_VERSION(2,6,34) && - dev->uses_numbered_reports) { - /* Work around a kernel bug. Chop off the first byte. */ - memmove(data, data+1, bytes_read); - bytes_read--; } +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); + return hid_read_timeout(dev, data, length, dev->blocking ? -1 : 0); } int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock) { - /* Do all non-blocking in userspace using poll(), since it looks - like there's a bug in the kernel in some versions where - read() will not return -1 on disconnection of the USB device */ - dev->blocking = !nonblock; - return 0; /* Success */ + + return 0; } int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length) { - int res; + 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*/); - res = ioctl(dev->device_handle, HIDIOCSFEATURE(length), data); if (res < 0) - perror("ioctl (SFEATURE)"); + return -1; - return res; + /* 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; + 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*/); - res = ioctl(dev->device_handle, HIDIOCGFEATURE(length), data); if (res < 0) - perror("ioctl (GFEATURE)"); + return -1; + if (skipped_report_id) + res++; return res; } @@ -765,29 +1236,63 @@ void HID_API_EXPORT hid_close(hid_device *dev) { if (!dev) return; - close(dev->device_handle); - free(dev); + + /* 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 get_device_string(dev, DEVICE_STRING_MANUFACTURER, string, 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 get_device_string(dev, DEVICE_STRING_PRODUCT, string, 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 get_device_string(dev, DEVICE_STRING_SERIAL, string, 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) { - return -1; + 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; } @@ -795,3 +1300,215 @@ 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 From 1aa559585ca210740edbcd7027de22b7affc7b82 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 27 Sep 2019 15:26:13 +0200 Subject: [PATCH 03/67] Added missing include --- src/slic3r/GUI/Mouse3DController.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index b62a00b6f..c4b3f78e4 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -2,8 +2,10 @@ #define slic3r_Mouse3DController_hpp_ #if ENABLE_3DCONNEXION_DEVICES +#include "libslic3r/Point.hpp" #include "hidapi/hidapi.h" + #include #include #include From 82fed1790a5ec9f0803aaa70ecce719c636a1e2c Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Mon, 30 Sep 2019 14:58:51 +0200 Subject: [PATCH 04/67] 3D connexion support on Linux: - Replace hidapi/linux/hid.c with the hidraw variant (link to udev) - Add CMakeLists.txt for hidapi, refactor - Add udev rules file (no installation so far) --- CMakeLists.txt | 19 - resources/udev/90-3dconnexion.rules | 23 + src/CMakeLists.txt | 1 + src/hidapi/CMakeLists.txt | 17 + src/hidapi/{ => include}/hidapi.h | 0 src/hidapi/linux/hid.c | 1717 ++++++++------------------ src/slic3r/CMakeLists.txt | 2 +- src/slic3r/GUI/Mouse3DController.cpp | 2 + 8 files changed, 544 insertions(+), 1237 deletions(-) create mode 100644 resources/udev/90-3dconnexion.rules create mode 100644 src/hidapi/CMakeLists.txt rename src/hidapi/{ => include}/hidapi.h (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index b9a7cf065..8c7fc12df 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -365,25 +365,6 @@ include_directories(${GLEW_INCLUDE_DIRS}) add_library(cereal INTERFACE) target_include_directories(cereal INTERFACE include) -# Find the hidapi library -if(WIN32) -add_library(hidapi STATIC - ${LIBDIR}/hidapi/win/hid.c -) -elseif (APPLE) -add_library(hidapi STATIC - ${LIBDIR}/hidapi/mac/hid.c -) -else () -add_library(hidapi STATIC - ${LIBDIR}/hidapi/linux/hid.c -) -endif () -set(HIDAPI_FOUND 1) -set(HIDAPI_INCLUDE_DIRS ${LIBDIR}/hidapi/) -set(HIDAPI_LIBRARIES hidapi) -include_directories(${HIDAPI_INCLUDE_DIRS}) - # l10n set(L10N_DIR "${SLIC3R_RESOURCES_DIR}/localization") add_custom_target(pot diff --git a/resources/udev/90-3dconnexion.rules b/resources/udev/90-3dconnexion.rules new file mode 100644 index 000000000..035d17125 --- /dev/null +++ b/resources/udev/90-3dconnexion.rules @@ -0,0 +1,23 @@ +# See src/slic3r/GUI/Mouse3DController.cpp for the list of devices + +# Logitech vendor devices +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c623", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c626", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c628", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c627", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c603", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c62b", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c621", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c625", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c629", MODE="0666" + +# 3D Connexion vendor devices +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c623", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c626", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c628", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c627", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c603", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c62b", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c621", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c625", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c629", MODE="0666" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 31cb24f24..dc1d5e38c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -14,6 +14,7 @@ add_subdirectory(qhull) add_subdirectory(Shiny) add_subdirectory(semver) add_subdirectory(libigl) +add_subdirectory(hidapi) # Adding libnest2d project for bin packing... set(LIBNEST2D_UNITTESTS ON CACHE BOOL "Force generating unittests for libnest2d") diff --git a/src/hidapi/CMakeLists.txt b/src/hidapi/CMakeLists.txt new file mode 100644 index 000000000..313fafbfd --- /dev/null +++ b/src/hidapi/CMakeLists.txt @@ -0,0 +1,17 @@ + +if (WIN32) + set(HIDAPI_IMPL win/hid.c) +elseif (APPLE) + set(HIDAPI_IMPL mac/hid.c) +else () + # Assume Linux or Unix other than Mac OS + set(HIDAPI_IMPL linux/hid.c) +endif() + +include_directories(include) + +add_library(hidapi STATIC ${HIDAPI_IMPL}) + +if (CMAKE_SYSTEM_NAME STREQUAL "Linux") + target_link_libraries(hidapi udev) +endif() diff --git a/src/hidapi/hidapi.h b/src/hidapi/include/hidapi.h similarity index 100% rename from src/hidapi/hidapi.h rename to src/hidapi/include/hidapi.h diff --git a/src/hidapi/linux/hid.c b/src/hidapi/linux/hid.c index 8cd5e3dca..56dac0fab 100644 --- a/src/hidapi/linux/hid.c +++ b/src/hidapi/linux/hid.c @@ -6,9 +6,7 @@ Signal 11 Software 8/22/2009 - Linux Version - 6/2/2010 - Libusb Version - 8/13/2010 - FreeBSD Version - 11/1/2011 + Linux Version - 6/2/2009 Copyright 2009, All Rights Reserved. @@ -23,13 +21,10 @@ http://github.com/signal11/hidapi . ********************************************************/ -#define _GNU_SOURCE /* needed for wcsdup() before glibc 2.10 */ - /* C */ #include #include #include -#include #include #include @@ -40,219 +35,124 @@ #include #include #include -#include -#include +#include -/* GNU / LibUSB */ -#include -#ifndef __ANDROID__ -#include -#endif +/* Linux */ +#include +#include +#include +#include #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; - } -} - +/* Definitions from linux/hidraw.h. Since these are new, some distros + may not have header files which contain them. */ +#ifndef HIDIOCSFEATURE +#define HIDIOCSFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x06, len) +#endif +#ifndef HIDIOCGFEATURE +#define HIDIOCGFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x07, len) #endif -#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; +/* USB HID device property names */ +const char *device_string_names[] = { + "manufacturer", + "product", + "serial", }; +/* Symbolic names for the properties above */ +enum device_string_id { + DEVICE_STRING_MANUFACTURER, + DEVICE_STRING_PRODUCT, + DEVICE_STRING_SERIAL, + + DEVICE_STRING_COUNT, +}; struct hid_device_ { - /* 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; + int device_handle; + int blocking; + int uses_numbered_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 __u32 kernel_version = 0; + +static __u32 detect_kernel_version(void) +{ + struct utsname name; + int major, minor, release; + int ret; + + uname(&name); + ret = sscanf(name.release, "%d.%d.%d", &major, &minor, &release); + if (ret == 3) { + return KERNEL_VERSION(major, minor, release); + } + + ret = sscanf(name.release, "%d.%d", &major, &minor); + if (ret == 2) { + return KERNEL_VERSION(major, minor, 0); + } + + printf("Couldn't determine kernel version from version string \"%s\"\n", name.release); + return 0; +} static hid_device *new_hid_device(void) { hid_device *dev = calloc(1, sizeof(hid_device)); + dev->device_handle = -1; dev->blocking = 1; - - pthread_mutex_init(&dev->mutex, NULL); - pthread_cond_init(&dev->condition, NULL); - pthread_barrier_init(&dev->barrier, NULL, 2); + dev->uses_numbered_reports = 0; 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); +/* The caller must free the returned string with free(). */ +static wchar_t *utf8_to_wchar_t(const char *utf8) +{ + wchar_t *ret = NULL; + + if (utf8) { + size_t wlen = mbstowcs(NULL, utf8, 0); + if ((size_t) -1 == wlen) { + return wcsdup(L""); + } + ret = calloc(wlen+1, sizeof(wchar_t)); + mbstowcs(ret, utf8, wlen+1); + ret[wlen] = 0x0000; + } + + return ret; } -#if 0 -/*TODO: Implement this funciton on hidapi/libusb.. */ -static void register_error(hid_device *dev, const char *op) +/* Get an attribute value from a udev_device and return it as a whar_t + string. The returned string must be freed with free() when done.*/ +static wchar_t *copy_udev_string(struct udev_device *dev, const char *udev_name) { - -} -#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; + return utf8_to_wchar_t(udev_device_get_sysattr_value(dev, udev_name)); } -/* 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) -{ +/* uses_numbered_reports() returns 1 if report_descriptor describes a device + which contains numbered reports. */ +static int uses_numbered_reports(__u8 *report_descriptor, __u32 size) { unsigned int i = 0; int size_code; int data_len, key_size; - int usage_found = 0, usage_page_found = 0; while (i < size) { int key = report_descriptor[i]; - int key_cmd = key & 0xfc; + + /* Check for the Report ID key */ + if (key == 0x85/*Report ID*/) { + /* This device has a Report ID, which means it uses + numbered reports. */ + return 1; + } //printf("key: %02hhx\n", key); @@ -291,390 +191,379 @@ static int get_usage(uint8_t *report_descriptor, size_t size, 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; - } - + /* Didn't find a Report ID key. Device doesn't use numbered reports. */ 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) +/* + * The caller is responsible for free()ing the (newly-allocated) character + * strings pointed to by serial_number_utf8 and product_name_utf8 after use. + */ +static int +parse_uevent_info(const char *uevent, int *bus_type, + unsigned short *vendor_id, unsigned short *product_id, + char **serial_number_utf8, char **product_name_utf8) { - char buf[512]; - int len; - wchar_t *str = NULL; + char *tmp = strdup(uevent); + char *saveptr = NULL; + char *line; + char *key; + char *value; -#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 + int found_id = 0; + int found_serial = 0; + int found_name = 0; - /* 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); + line = strtok_r(tmp, "\n", &saveptr); + while (line != NULL) { + /* line: "KEY=value" */ + key = line; + value = strchr(line, '='); + if (!value) { + goto next_line; + } + *value = '\0'; + value++; - /* Get the string from libusb. */ - len = libusb_get_string_descriptor(dev, - idx, - lang, - (unsigned char*)buf, - sizeof(buf)); - if (len < 0) - return NULL; + if (strcmp(key, "HID_ID") == 0) { + /** + * type vendor product + * HID_ID=0003:000005AC:00008242 + **/ + int ret = sscanf(value, "%x:%hx:%hx", bus_type, vendor_id, product_id); + if (ret == 3) { + found_id = 1; + } + } else if (strcmp(key, "HID_NAME") == 0) { + /* The caller has to free the product name */ + *product_name_utf8 = strdup(value); + found_name = 1; + } else if (strcmp(key, "HID_UNIQ") == 0) { + /* The caller has to free the serial number */ + *serial_number_utf8 = strdup(value); + found_serial = 1; + } -#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; +next_line: + line = strtok_r(NULL, "\n", &saveptr); } - /* 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; + free(tmp); + return (found_id && found_name && found_serial); } -static char *make_path(libusb_device *dev, int interface_number) + +static int get_device_string(hid_device *dev, enum device_string_id key, wchar_t *string, size_t maxlen) { - 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'; + struct udev *udev; + struct udev_device *udev_dev, *parent, *hid_dev; + struct stat s; + int ret = -1; + char *serial_number_utf8 = NULL; + char *product_name_utf8 = NULL; - return strdup(str); + /* Create the udev object */ + udev = udev_new(); + if (!udev) { + printf("Can't create udev\n"); + return -1; + } + + /* Get the dev_t (major/minor numbers) from the file handle. */ + ret = fstat(dev->device_handle, &s); + if (-1 == ret) + return ret; + /* Open a udev device from the dev_t. 'c' means character device. */ + udev_dev = udev_device_new_from_devnum(udev, 'c', s.st_rdev); + if (udev_dev) { + hid_dev = udev_device_get_parent_with_subsystem_devtype( + udev_dev, + "hid", + NULL); + if (hid_dev) { + unsigned short dev_vid; + unsigned short dev_pid; + int bus_type; + size_t retm; + + ret = parse_uevent_info( + udev_device_get_sysattr_value(hid_dev, "uevent"), + &bus_type, + &dev_vid, + &dev_pid, + &serial_number_utf8, + &product_name_utf8); + + if (bus_type == BUS_BLUETOOTH) { + switch (key) { + case DEVICE_STRING_MANUFACTURER: + wcsncpy(string, L"", maxlen); + ret = 0; + break; + case DEVICE_STRING_PRODUCT: + retm = mbstowcs(string, product_name_utf8, maxlen); + ret = (retm == (size_t)-1)? -1: 0; + break; + case DEVICE_STRING_SERIAL: + retm = mbstowcs(string, serial_number_utf8, maxlen); + ret = (retm == (size_t)-1)? -1: 0; + break; + case DEVICE_STRING_COUNT: + default: + ret = -1; + break; + } + } + else { + /* This is a USB device. Find its parent USB Device node. */ + parent = udev_device_get_parent_with_subsystem_devtype( + udev_dev, + "usb", + "usb_device"); + if (parent) { + const char *str; + const char *key_str = NULL; + + if (key >= 0 && key < DEVICE_STRING_COUNT) { + key_str = device_string_names[key]; + } else { + ret = -1; + goto end; + } + + str = udev_device_get_sysattr_value(parent, key_str); + if (str) { + /* Convert the string from UTF-8 to wchar_t */ + retm = mbstowcs(string, str, maxlen); + ret = (retm == (size_t)-1)? -1: 0; + goto end; + } + } + } + } + } + +end: + free(serial_number_utf8); + free(product_name_utf8); + + udev_device_unref(udev_dev); + /* parent and hid_dev don't need to be (and can't be) unref'd. + I'm not sure why, but they'll throw double-free() errors. */ + udev_unref(udev); + + return ret; } - int HID_API_EXPORT hid_init(void) { - if (!usb_context) { - const char *locale; + 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, ""); - /* Set the locale if it's not set. */ - locale = setlocale(LC_CTYPE, NULL); - if (!locale) - setlocale(LC_CTYPE, ""); - } + kernel_version = detect_kernel_version(); return 0; } int HID_API_EXPORT hid_exit(void) { - if (usb_context) { - libusb_exit(usb_context); - usb_context = NULL; - } - + /* Nothing to do for this in the Linux/hidraw implementation. */ return 0; } + struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id) { - libusb_device **devs; - libusb_device *dev; - libusb_device_handle *handle; - ssize_t num_devs; - int i = 0; + struct udev *udev; + struct udev_enumerate *enumerate; + struct udev_list_entry *devices, *dev_list_entry; struct hid_device_info *root = NULL; /* return object */ struct hid_device_info *cur_dev = NULL; + struct hid_device_info *prev_dev = NULL; /* previous device */ - if(hid_init() < 0) + hid_init(); + + /* Create the udev object */ + udev = udev_new(); + if (!udev) { + printf("Can't create udev\n"); 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); + /* Create a list of the devices in the 'hidraw' subsystem. */ + enumerate = udev_enumerate_new(udev); + udev_enumerate_add_match_subsystem(enumerate, "hidraw"); + udev_enumerate_scan_devices(enumerate); + devices = udev_enumerate_get_list_entry(enumerate); + /* For each item, see if it matches the vid/pid, and if so + create a udev_device record for it */ + udev_list_entry_foreach(dev_list_entry, devices) { + const char *sysfs_path; + const char *dev_path; + const char *str; + struct udev_device *raw_dev; /* The device's hidraw udev node. */ + struct udev_device *hid_dev; /* The device's HID udev node. */ + struct udev_device *usb_dev; /* The device's USB udev node. */ + struct udev_device *intf_dev; /* The device's interface (in the USB sense). */ + unsigned short dev_vid; + unsigned short dev_pid; + char *serial_number_utf8 = NULL; + char *product_name_utf8 = NULL; + int bus_type; + int result; + + /* Get the filename of the /sys entry for the device + and create a udev_device object (dev) representing it */ + sysfs_path = udev_list_entry_get_name(dev_list_entry); + raw_dev = udev_device_new_from_syspath(udev, sysfs_path); + dev_path = udev_device_get_devnode(raw_dev); + + hid_dev = udev_device_get_parent_with_subsystem_devtype( + raw_dev, + "hid", + NULL); + + if (!hid_dev) { + /* Unable to find parent hid device. */ + goto next; + } + + result = parse_uevent_info( + udev_device_get_sysattr_value(hid_dev, "uevent"), + &bus_type, + &dev_vid, + &dev_pid, + &serial_number_utf8, + &product_name_utf8); + + if (!result) { + /* parse_uevent_info() failed for at least one field. */ + goto next; + } + + if (bus_type != BUS_USB && bus_type != BUS_BLUETOOTH) { + /* We only know how to handle USB and BT devices. */ + goto next; + } + + /* Check the VID/PID against the arguments */ + if ((vendor_id == 0x0 || vendor_id == dev_vid) && + (product_id == 0x0 || product_id == dev_pid)) { + struct hid_device_info *tmp; + + /* VID/PID match. Create the record. */ + tmp = malloc(sizeof(struct hid_device_info)); + if (cur_dev) { + cur_dev->next = tmp; + } + else { + root = tmp; + } + prev_dev = cur_dev; + cur_dev = tmp; + + /* Fill out the record */ + cur_dev->next = NULL; + cur_dev->path = dev_path? strdup(dev_path): NULL; + + /* VID/PID */ + cur_dev->vendor_id = dev_vid; + cur_dev->product_id = dev_pid; + + /* Serial Number */ + cur_dev->serial_number = utf8_to_wchar_t(serial_number_utf8); + + /* Release Number */ + cur_dev->release_number = 0x0; + + /* Interface Number */ + cur_dev->interface_number = -1; + + switch (bus_type) { + case BUS_USB: + /* The device pointed to by raw_dev contains information about + the hidraw device. In order to get information about the + USB device, get the parent device with the + subsystem/devtype pair of "usb"/"usb_device". This will + be several levels up the tree, but the function will find + it. */ + usb_dev = udev_device_get_parent_with_subsystem_devtype( + raw_dev, + "usb", + "usb_device"); + + if (!usb_dev) { + /* Free this device */ + free(cur_dev->serial_number); + free(cur_dev->path); + free(cur_dev); + + /* Take it off the device list. */ + if (prev_dev) { + prev_dev->next = NULL; + cur_dev = prev_dev; + } + else { + cur_dev = root = NULL; + } + + goto next; + } + + /* Manufacturer and Product strings */ + cur_dev->manufacturer_string = copy_udev_string(usb_dev, device_string_names[DEVICE_STRING_MANUFACTURER]); + cur_dev->product_string = copy_udev_string(usb_dev, device_string_names[DEVICE_STRING_PRODUCT]); + + /* Release Number */ + str = udev_device_get_sysattr_value(usb_dev, "bcdDevice"); + cur_dev->release_number = (str)? strtol(str, NULL, 16): 0x0; + + /* Get a handle to the interface's udev node. */ + intf_dev = udev_device_get_parent_with_subsystem_devtype( + raw_dev, + "usb", + "usb_interface"); + if (intf_dev) { + str = udev_device_get_sysattr_value(intf_dev, "bInterfaceNumber"); + cur_dev->interface_number = (str)? strtol(str, NULL, 16): -1; + } + + break; + + case BUS_BLUETOOTH: + /* Manufacturer and Product strings */ + cur_dev->manufacturer_string = wcsdup(L""); + cur_dev->product_string = utf8_to_wchar_t(product_name_utf8); + + break; + + default: + /* Unknown device type - this should never happen, as we + * check for USB and Bluetooth devices above */ + break; + } + } + + next: + free(serial_number_utf8); + free(product_name_utf8); + udev_device_unref(raw_dev); + /* hid_dev, usb_dev and intf_dev don't need to be (and can't be) + unref()d. It will cause a double-free() error. I'm not + sure why. */ + } + /* Free the enumerator and udev objects. */ + udev_enumerate_unref(enumerate); + udev_unref(udev); return root; } @@ -705,8 +594,7 @@ hid_device * hid_open(unsigned short vendor_id, unsigned short product_id, const 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) { + if (wcscmp(serial_number, cur_dev->serial_number) == 0) { path_to_open = cur_dev->path; break; } @@ -729,272 +617,49 @@ hid_device * hid_open(unsigned short vendor_id, unsigned short product_id, const 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; + hid_init(); 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); + /* OPEN HERE */ + dev->device_handle = open(path, O_RDWR); /* If we have a good handle, return it. */ - if (good_open) { + if (dev->device_handle > 0) { + + /* Get the report descriptor */ + int res, desc_size = 0; + struct hidraw_report_descriptor rpt_desc; + + memset(&rpt_desc, 0x0, sizeof(rpt_desc)); + + /* Get Report Descriptor Size */ + res = ioctl(dev->device_handle, HIDIOCGRDESCSIZE, &desc_size); + if (res < 0) + perror("HIDIOCGRDESCSIZE"); + + + /* Get Report Descriptor */ + rpt_desc.size = desc_size; + res = ioctl(dev->device_handle, HIDIOCGRDESC, &rpt_desc); + if (res < 0) { + perror("HIDIOCGRDESC"); + } else { + /* Determine if this device uses numbered reports. */ + dev->uses_numbered_reports = + uses_numbered_reports(rpt_desc.value, + rpt_desc.size); + } + return dev; } else { /* Unable to open any devices. */ - free_hid_device(dev); + free(dev); return NULL; } } @@ -1002,231 +667,95 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path) 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; + int bytes_written; - if (report_number == 0x0) { - data++; - length--; - skipped_report_id = 1; - } + bytes_written = write(dev->device_handle, data, length); - - 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); + return bytes_written; } int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) { - int bytes_read = -1; + int bytes_read; -#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 + if (milliseconds >= 0) { + /* Milliseconds is either 0 (non-blocking) or > 0 (contains + a valid timeout). In both cases we want to call poll() + and wait for data to arrive. Don't rely on non-blocking + operation (O_NONBLOCK) since some kernels don't seem to + properly report device disconnection through read() when + in non-blocking mode. */ + int ret; + struct pollfd fds; - 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); + fds.fd = dev->device_handle; + fds.events = POLLIN; + fds.revents = 0; + ret = poll(&fds, 1, milliseconds); + if (ret == -1 || ret == 0) { + /* Error or timeout */ + return ret; } - if (dev->input_reports) { - bytes_read = return_data(dev, data, length); + else { + /* Check for errors on the file descriptor. This will + indicate a device disconnection. */ + if (fds.revents & (POLLERR | POLLHUP | POLLNVAL)) + return -1; } } - 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 = read(dev->device_handle, data, length); + if (bytes_read < 0 && (errno == EAGAIN || errno == EINPROGRESS)) bytes_read = 0; - } -ret: - pthread_mutex_unlock(&dev->mutex); - pthread_cleanup_pop(0); + if (bytes_read >= 0 && + kernel_version != 0 && + kernel_version < KERNEL_VERSION(2,6,34) && + dev->uses_numbered_reports) { + /* Work around a kernel bug. Chop off the first byte. */ + memmove(data, data+1, bytes_read); + bytes_read--; + } return bytes_read; } int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length) { - return hid_read_timeout(dev, data, length, dev->blocking ? -1 : 0); + 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; + /* Do all non-blocking in userspace using poll(), since it looks + like there's a bug in the kernel in some versions where + read() will not return -1 on disconnection of the USB device */ - return 0; + dev->blocking = !nonblock; + return 0; /* Success */ } int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length) { - int res = -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*/); + int res; + res = ioctl(dev->device_handle, HIDIOCSFEATURE(length), data); if (res < 0) - return -1; + perror("ioctl (SFEATURE)"); - /* Account for the report ID */ - if (skipped_report_id) - length++; - - return length; + return res; } 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*/); + int res; + res = ioctl(dev->device_handle, HIDIOCGFEATURE(length), data); if (res < 0) - return -1; + perror("ioctl (GFEATURE)"); - if (skipped_report_id) - res++; return res; } @@ -1236,63 +765,29 @@ 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); + close(dev->device_handle); + free(dev); } int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) { - return hid_get_indexed_string(dev, dev->manufacturer_index, string, maxlen); + return get_device_string(dev, DEVICE_STRING_MANUFACTURER, string, maxlen); } int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) { - return hid_get_indexed_string(dev, dev->product_index, string, maxlen); + return get_device_string(dev, DEVICE_STRING_PRODUCT, string, maxlen); } int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) { - return hid_get_indexed_string(dev, dev->serial_index, string, maxlen); + return get_device_string(dev, DEVICE_STRING_SERIAL, string, maxlen); } int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen) { - 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; + return -1; } @@ -1300,215 +795,3 @@ 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/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 6218f3837..098dca241 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -169,7 +169,7 @@ add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES}) encoding_check(libslic3r_gui) -target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui ${GLEW_LIBRARIES} ${HIDAPI_LIBRARIES}) +target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui ${GLEW_LIBRARIES} hidapi) if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY) add_precompiled_header(libslic3r_gui pchheader.hpp FORCEINCLUDE) endif () diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 43faf852b..f00f03c13 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -9,6 +9,8 @@ #include +// WARN: If updating these lists, please also update resources/udev/90-3dconnexion.rules + static const std::vector _3DCONNEXION_VENDORS = { 0x046d, // LOGITECH = 1133 // Logitech (3Dconnexion is made by Logitech) From 520a51c62fc958716b867a9138312f8f466cd488 Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Mon, 30 Sep 2019 15:20:23 +0200 Subject: [PATCH 05/67] hidapi: Fix include path --- src/CMakeLists.txt | 3 ++- src/slic3r/GUI/Mouse3DController.hpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dc1d5e38c..c087e57e2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -14,7 +14,6 @@ add_subdirectory(qhull) add_subdirectory(Shiny) add_subdirectory(semver) add_subdirectory(libigl) -add_subdirectory(hidapi) # Adding libnest2d project for bin packing... set(LIBNEST2D_UNITTESTS ON CACHE BOOL "Force generating unittests for libnest2d") @@ -24,6 +23,8 @@ add_subdirectory(libslic3r) if (SLIC3R_GUI) add_subdirectory(imgui) + add_subdirectory(hidapi) + include_directories(hidapi/include) if(WIN32) message(STATUS "WXWIN environment set to: $ENV{WXWIN}") diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index c4b3f78e4..9c7f1ce5e 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -4,7 +4,7 @@ #if ENABLE_3DCONNEXION_DEVICES #include "libslic3r/Point.hpp" -#include "hidapi/hidapi.h" +#include "hidapi.h" #include #include From d58dedd4596e580856ef0f5009dc37fb83583746 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 30 Sep 2019 15:58:45 +0200 Subject: [PATCH 06/67] ENABLE_3DCONNEXION_DEVICES -> Increased limit of device buttons --- src/slic3r/GUI/GLCanvas3D.cpp | 2 +- src/slic3r/GUI/Mouse3DController.cpp | 37 ++++++++++++++++------------ src/slic3r/GUI/Mouse3DController.hpp | 10 +++++++- 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 4588084fc..ea7992783 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2551,7 +2551,7 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt) #if ENABLE_3DCONNEXION_DEVICES // try to filter out events coming from mouse 3d controller const Mouse3DController& controller = wxGetApp().plater()->get_mouse3d_controller(); - if (controller.has_rotation() || controller.has_translation()) + if (controller.has_translation_or_rotation()) return; #endif // ENABLE_3DCONNEXION_DEVICES diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index f00f03c13..10022915a 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -30,7 +30,7 @@ static const std::vector _3DCONNEXION_DEVICES = 0xc629 // SPACEPILOTPRO = 50729 }; -static const unsigned int _3DCONNEXION_BUTTONS_COUNT = 2; +static const unsigned int _3DCONNEXION_MAX_BUTTONS_COUNT = 256; namespace Slic3r { namespace GUI { @@ -41,7 +41,6 @@ const float Mouse3DController::State::DefaultRotationScale = 1.0; Mouse3DController::State::State() : m_translation(Vec3d::Zero()) , m_rotation(Vec3f::Zero()) - , m_buttons(_3DCONNEXION_BUTTONS_COUNT, false) , m_translation_scale(DefaultTranslationScale) , m_rotation_scale(DefaultRotationScale) { @@ -62,8 +61,7 @@ void Mouse3DController::State::set_rotation(const Vec3f& rotation) void Mouse3DController::State::set_button(unsigned int id) { std::lock_guard lock(m_mutex); - if (id < _3DCONNEXION_BUTTONS_COUNT) - m_buttons[id] = true; + m_buttons.push_back(id); } bool Mouse3DController::State::has_translation() const @@ -71,21 +69,23 @@ bool Mouse3DController::State::has_translation() const std::lock_guard lock(m_mutex); return !m_translation.isApprox(Vec3d::Zero()); } + bool Mouse3DController::State::has_rotation() const { std::lock_guard lock(m_mutex); return !m_rotation.isApprox(Vec3f::Zero()); } +bool Mouse3DController::State::has_translation_or_rotation() const +{ + std::lock_guard lock(m_mutex); + return !m_translation.isApprox(Vec3d::Zero()) && !m_rotation.isApprox(Vec3f::Zero()); +} + bool Mouse3DController::State::has_any_button() const { std::lock_guard lock(m_mutex); - for (int i = 0; i < _3DCONNEXION_BUTTONS_COUNT; ++i) - { - if (m_buttons[i]) - return true; - } - return false; + return !m_buttons.empty(); } bool Mouse3DController::State::apply(GLCanvas3D& canvas) @@ -116,12 +116,17 @@ bool Mouse3DController::State::apply(GLCanvas3D& canvas) if (has_any_button()) { - if (m_buttons[0]) - canvas.set_camera_zoom(1.0); - else if (m_buttons[1]) - canvas.set_camera_zoom(-1.0); + for (unsigned int i : m_buttons) + { + switch (i) + { + case 0: { canvas.set_camera_zoom(1.0); break; } + case 1: { canvas.set_camera_zoom(-1.0); break; } + default: { break; } + } + } - m_buttons = std::vector(_3DCONNEXION_BUTTONS_COUNT, false); + m_buttons.clear(); ret = true; } @@ -314,7 +319,7 @@ void Mouse3DController::collect_input() } case Button: { - for (unsigned int i = 0; i < _3DCONNEXION_BUTTONS_COUNT; ++i) + for (unsigned int i = 0; i < _3DCONNEXION_MAX_BUTTONS_COUNT; ++i) { if (retrieved_data[1] & (0x1 << i)) m_state.set_button(i); diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index 9c7f1ce5e..64fa34a44 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -26,7 +26,7 @@ class Mouse3DController Vec3d m_translation; Vec3f m_rotation; - std::vector m_buttons; + std::vector m_buttons; double m_translation_scale; float m_rotation_scale; @@ -40,8 +40,15 @@ class Mouse3DController bool has_translation() const; bool has_rotation() const; + bool has_translation_or_rotation() const; bool has_any_button() const; + double get_translation_scale() const { return m_translation_scale; } + void set_translation_scale(double scale) { m_translation_scale = scale; } + + float get_rotation_scale() const { return m_rotation_scale; } + void set_rotation_scale(float scale) { m_rotation_scale = scale; } + // return true if any change to the camera took place bool apply(GLCanvas3D& canvas); }; @@ -71,6 +78,7 @@ public: bool has_translation() const { return m_state.has_translation(); } bool has_rotation() const { return m_state.has_rotation(); } + bool has_translation_or_rotation() const { return m_state.has_translation_or_rotation(); } bool has_any_button() const { return m_state.has_any_button(); } bool apply() From 36d3f90fad0f79e3fefbadacd3394d7d49687ad1 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 2 Oct 2019 15:55:26 +0200 Subject: [PATCH 07/67] ENABLE_3DCONNEXION_DEVICES - Added imgui dialog to edit 3Dconnexion device parameters --- src/slic3r/GUI/GLCanvas3D.cpp | 19 ++++++ src/slic3r/GUI/ImGuiWrapper.cpp | 5 ++ src/slic3r/GUI/KBShortcutsDialog.cpp | 3 + src/slic3r/GUI/Mouse3DController.cpp | 91 +++++++++++++++++++++++++--- src/slic3r/GUI/Mouse3DController.hpp | 16 ++++- 5 files changed, 125 insertions(+), 9 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 2944ffccd..562a939fb 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1636,6 +1636,10 @@ void GLCanvas3D::render() m_camera.debug_render(); #endif // ENABLE_CAMERA_STATISTICS +#if ENABLE_3DCONNEXION_DEVICES + wxGetApp().plater()->get_mouse3d_controller().render_settings_dialog(); +#endif // ENABLE_3DCONNEXION_DEVICES + wxGetApp().imgui()->render(); m_canvas->SwapBuffers(); @@ -2374,6 +2378,21 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) #endif /* __APPLE__ */ post_event(SimpleEvent(EVT_GLTOOLBAR_COPY)); break; + +#if ENABLE_3DCONNEXION_DEVICES +#ifdef __APPLE__ + case 'm': + case 'M': +#else /* __APPLE__ */ + case WXK_CONTROL_M: +#endif /* __APPLE__ */ + { + Mouse3DController& controller = wxGetApp().plater()->get_mouse3d_controller(); + controller.show_settings_dialog(!controller.is_settings_dialog_shown()); + break; + } +#endif // ENABLE_3DCONNEXION_DEVICES + #ifdef __APPLE__ case 'v': case 'V': diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 8e4d9eebf..7d7e82685 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -528,6 +528,11 @@ void ImGuiWrapper::init_style() // Slider set_color(ImGuiCol_SliderGrab, COL_ORANGE_DARK); set_color(ImGuiCol_SliderGrabActive, COL_ORANGE_LIGHT); + +#if ENABLE_3DCONNEXION_DEVICES + // Separator + set_color(ImGuiCol_Separator, COL_ORANGE_LIGHT); +#endif // ENABLE_3DCONNEXION_DEVICES } void ImGuiWrapper::render_draw_data(ImDrawData *draw_data) diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index e3ef335e6..4bd56ee76 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -157,6 +157,9 @@ void KBShortcutsDialog::fill_shortcuts() plater_shortcuts.push_back(Shortcut("Z", L("Zoom to selected object"))); plater_shortcuts.push_back(Shortcut("I", L("Zoom in"))); plater_shortcuts.push_back(Shortcut("O", L("Zoom out"))); +#if ENABLE_3DCONNEXION_DEVICES + plater_shortcuts.push_back(Shortcut(ctrl+"M", L("Show/Hide 3Dconnexion devices settings dialog"))); +#endif // ENABLE_3DCONNEXION_DEVICES plater_shortcuts.push_back(Shortcut("ESC", L("Unselect gizmo / Clear selection"))); #if ENABLE_RENDER_PICKING_PASS // Don't localize debugging texts. diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 10022915a..18c17f7c2 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -9,6 +9,10 @@ #include +#include + +#include "I18N.hpp" + // WARN: If updating these lists, please also update resources/udev/90-3dconnexion.rules static const std::vector _3DCONNEXION_VENDORS = @@ -64,6 +68,30 @@ void Mouse3DController::State::set_button(unsigned int id) m_buttons.push_back(id); } +void Mouse3DController::State::reset_buttons() +{ + std::lock_guard lock(m_mutex); + return m_buttons.clear(); +} + +const Vec3d& Mouse3DController::State::get_translation() const +{ + std::lock_guard lock(m_mutex); + return m_translation; +} + +const Vec3f& Mouse3DController::State::get_rotation() const +{ + std::lock_guard lock(m_mutex); + return m_rotation; +} + +const std::vector& Mouse3DController::State::get_buttons() const +{ + std::lock_guard lock(m_mutex); + return m_buttons; +} + bool Mouse3DController::State::has_translation() const { std::lock_guard lock(m_mutex); @@ -98,25 +126,28 @@ bool Mouse3DController::State::apply(GLCanvas3D& canvas) if (has_translation()) { - camera.set_target(camera.get_target() + m_translation_scale * (m_translation(0) * camera.get_dir_right() + m_translation(1) * camera.get_dir_forward() + m_translation(2) * camera.get_dir_up())); - m_translation = Vec3d::Zero(); + Vec3d translation = get_translation(); + camera.set_target(camera.get_target() + m_translation_scale * (translation(0) * camera.get_dir_right() + translation(1) * camera.get_dir_forward() + translation(2) * camera.get_dir_up())); + set_translation(Vec3d::Zero()); ret = true; } if (has_rotation()) { - float theta = m_rotation_scale * m_rotation(0); - float phi = m_rotation_scale * m_rotation(2); + Vec3f rotation = get_rotation(); + float theta = m_rotation_scale * rotation(0); + float phi = m_rotation_scale * rotation(2); float sign = camera.inverted_phi ? -1.0f : 1.0f; camera.phi += sign * phi; camera.set_theta(camera.get_theta() + theta, wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA); - m_rotation = Vec3f::Zero(); + set_rotation(Vec3f::Zero()); ret = true; } if (has_any_button()) { - for (unsigned int i : m_buttons) + std::vector buttons = get_buttons(); + for (unsigned int i : buttons) { switch (i) { @@ -126,7 +157,7 @@ bool Mouse3DController::State::apply(GLCanvas3D& canvas) } } - m_buttons.clear(); + reset_buttons(); ret = true; } @@ -138,6 +169,7 @@ Mouse3DController::Mouse3DController() , m_canvas(nullptr) , m_device(nullptr) , m_running(false) + , m_settings_dialog(false) { } @@ -174,6 +206,51 @@ void Mouse3DController::shutdown() m_initialized = false; } +void Mouse3DController::render_settings_dialog() const +{ + if ((m_canvas == nullptr) || !m_running || !m_settings_dialog) + return; + + ImGuiWrapper& imgui = *wxGetApp().imgui(); + + std::lock_guard lock(m_mutex); + Size cnv_size = m_canvas->get_canvas_size(); + + imgui.set_next_window_pos(0.5f * (float)cnv_size.get_width(), 0.5f * (float)cnv_size.get_height(), ImGuiCond_Always, 0.5f, 0.5f); + imgui.set_next_window_bg_alpha(0.5f); + + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + + imgui.begin(_(L("3Dconnexion settings")), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse); + + std::vector product(1024, 0); + hid_get_product_string(m_device, product.data(), 1024); + + const ImVec4& color = ImGui::GetStyleColorVec4(ImGuiCol_Separator); + ImGui::PushStyleColor(ImGuiCol_Text, color); + imgui.text(_(L("Device:"))); + ImGui::PopStyleColor(); + ImGui::SameLine(); + imgui.text(boost::nowide::narrow(product.data())); + ImGui::Separator(); + + ImGui::PushStyleColor(ImGuiCol_Text, color); + imgui.text(_(L("Speed:"))); + ImGui::PopStyleColor(); + + float translation = (float)m_state.get_translation_scale() / State::DefaultTranslationScale; + if (ImGui::SliderFloat(_(L("Translation")), &translation, 0.5f, 2.0f, "%.1f")) + m_state.set_translation_scale(State::DefaultTranslationScale * (double)translation); + + float rotation = (float)m_state.get_rotation_scale() / State::DefaultRotationScale; + if (ImGui::SliderFloat(_(L("Rotation")), &rotation, 0.5f, 2.0f, "%.1f")) + m_state.set_rotation_scale(State::DefaultRotationScale * rotation); + + imgui.end(); + + ImGui::PopStyleVar(); +} + void Mouse3DController::connect_device() { if (m_device != nullptr) diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index 64fa34a44..778a3efed 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -19,9 +19,11 @@ class Mouse3DController { class State { + public: static const double DefaultTranslationScale; static const float DefaultRotationScale; + private: mutable std::mutex m_mutex; Vec3d m_translation; @@ -37,6 +39,11 @@ class Mouse3DController void set_translation(const Vec3d& translation); void set_rotation(const Vec3f& rotation); void set_button(unsigned int id); + void reset_buttons(); + + const Vec3d& get_translation() const; + const Vec3f& get_rotation() const; + const std::vector& get_buttons() const; bool has_translation() const; bool has_rotation() const; @@ -54,12 +61,13 @@ class Mouse3DController }; bool m_initialized; - State m_state; + mutable State m_state; std::thread m_thread; GLCanvas3D* m_canvas; - std::mutex m_mutex; + mutable std::mutex m_mutex; hid_device* m_device; bool m_running; + bool m_settings_dialog; public: Mouse3DController(); @@ -87,6 +95,10 @@ public: return (m_canvas != nullptr) ? m_state.apply(*m_canvas) : false; } + bool is_settings_dialog_shown() const { return m_settings_dialog; } + void show_settings_dialog(bool show) { m_settings_dialog = show; } + void render_settings_dialog() const; + private: void connect_device(); void disconnect_device(); From a5543040384541aa0a9ab15cb51d6e3446d36c03 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 3 Oct 2019 10:26:28 +0200 Subject: [PATCH 08/67] ENABLE_3DCONNEXION_DEVICES -> Serialization of mouse device parameters into config --- src/slic3r/GUI/AppConfig.cpp | 42 ++++++++++++++++++++++++++ src/slic3r/GUI/AppConfig.hpp | 6 ++++ src/slic3r/GUI/Mouse3DController.cpp | 44 +++++++++++++++++++++++----- src/slic3r/GUI/Mouse3DController.hpp | 1 + 4 files changed, 86 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/AppConfig.cpp b/src/slic3r/GUI/AppConfig.cpp index 5a165e8ae..2d5ee6f5f 100644 --- a/src/slic3r/GUI/AppConfig.cpp +++ b/src/slic3r/GUI/AppConfig.cpp @@ -268,6 +268,48 @@ void AppConfig::set_recent_projects(const std::vector& recent_proje } } +#if ENABLE_3DCONNEXION_DEVICES +void AppConfig::set_mouse_device(const std::string& name, double translation_speed, float rotation_speed) +{ + std::string key = std::string("mouse_device:") + name; + auto it = m_storage.find(key); + if (it == m_storage.end()) + it = m_storage.insert(std::map>::value_type(key, std::map())).first; + + it->second.clear(); + it->second["translation_speed"] = std::to_string(translation_speed); + it->second["rotation_speed"] = std::to_string(rotation_speed); +} + +bool AppConfig::get_mouse_device_translation_speed(const std::string& name, double& translation_speed) +{ + std::string key = std::string("mouse_device:") + name; + auto it = m_storage.find(key); + if (it == m_storage.end()) + return false; + + auto it_val = it->second.find("translation_speed"); + if (it_val == it->second.end()) + return false; + + translation_speed = ::atof(it_val->second.c_str()); +} + +bool AppConfig::get_mouse_device_rotation_speed(const std::string& name, float& rotation_speed) +{ + std::string key = std::string("mouse_device:") + name; + auto it = m_storage.find(key); + if (it == m_storage.end()) + return false; + + auto it_val = it->second.find("rotation_speed"); + if (it_val == it->second.end()) + return false; + + rotation_speed = (float)::atof(it_val->second.c_str()); +} +#endif // ENABLE_3DCONNEXION_DEVICES + void AppConfig::update_config_dir(const std::string &dir) { this->set("recent", "config_directory", dir); diff --git a/src/slic3r/GUI/AppConfig.hpp b/src/slic3r/GUI/AppConfig.hpp index 8ad17b9db..4959b5433 100644 --- a/src/slic3r/GUI/AppConfig.hpp +++ b/src/slic3r/GUI/AppConfig.hpp @@ -125,6 +125,12 @@ public: std::vector get_recent_projects() const; void set_recent_projects(const std::vector& recent_projects); +#if ENABLE_3DCONNEXION_DEVICES + void set_mouse_device(const std::string& name, double translation_speed, float rotation_speed); + bool get_mouse_device_translation_speed(const std::string& name, double& translation_speed); + bool get_mouse_device_rotation_speed(const std::string& name, float& rotation_speed); +#endif // ENABLE_3DCONNEXION_DEVICES + private: // Map of section, name -> value std::map> m_storage; diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 18c17f7c2..c7bf89b4d 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -6,6 +6,7 @@ #include "GLCanvas3D.hpp" #include "GUI_App.hpp" #include "PresetBundle.hpp" +#include "AppConfig.hpp" #include @@ -34,8 +35,6 @@ static const std::vector _3DCONNEXION_DEVICES = 0xc629 // SPACEPILOTPRO = 50729 }; -static const unsigned int _3DCONNEXION_MAX_BUTTONS_COUNT = 256; - namespace Slic3r { namespace GUI { @@ -168,6 +167,7 @@ Mouse3DController::Mouse3DController() : m_initialized(false) , m_canvas(nullptr) , m_device(nullptr) + , m_device_str("") , m_running(false) , m_settings_dialog(false) { @@ -223,15 +223,12 @@ void Mouse3DController::render_settings_dialog() const imgui.begin(_(L("3Dconnexion settings")), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse); - std::vector product(1024, 0); - hid_get_product_string(m_device, product.data(), 1024); - const ImVec4& color = ImGui::GetStyleColorVec4(ImGuiCol_Separator); ImGui::PushStyleColor(ImGuiCol_Text, color); imgui.text(_(L("Device:"))); ImGui::PopStyleColor(); ImGui::SameLine(); - imgui.text(boost::nowide::narrow(product.data())); + imgui.text(m_device_str); ImGui::Separator(); ImGui::PushStyleColor(ImGuiCol_Text, color); @@ -306,6 +303,22 @@ void Mouse3DController::connect_device() // Open the 3Dconnexion device using the VID, PID m_device = hid_open(vendor_id, product_id, nullptr); + + if (m_device != nullptr) + { + std::vector product(1024, 0); + hid_get_product_string(m_device, product.data(), 1024); + m_device_str = boost::nowide::narrow(product.data()); + + // gets device parameters from the config, if present + double translation = 1.0; + float rotation = 1.0; + wxGetApp().app_config->get_mouse_device_translation_speed(m_device_str, translation); + wxGetApp().app_config->get_mouse_device_rotation_speed(m_device_str, rotation); + // clamp to valid values + m_state.set_translation_scale(State::DefaultTranslationScale * std::max(0.5, std::min(2.0, translation))); + m_state.set_rotation_scale(State::DefaultRotationScale * std::max(0.5f, std::min(2.0f, rotation))); + } } void Mouse3DController::disconnect_device() @@ -313,9 +326,14 @@ void Mouse3DController::disconnect_device() if (m_device == nullptr) return; + // stores current device parameters into the config + wxGetApp().app_config->set_mouse_device(m_device_str, m_state.get_translation_scale() / State::DefaultTranslationScale, m_state.get_rotation_scale() / State::DefaultRotationScale); + wxGetApp().app_config->save(); + // Close the 3Dconnexion device hid_close(m_device); m_device = nullptr; + m_device_str = ""; } void Mouse3DController::start() @@ -396,12 +414,24 @@ void Mouse3DController::collect_input() } case Button: { - for (unsigned int i = 0; i < _3DCONNEXION_MAX_BUTTONS_COUNT; ++i) + // Because of lack of documentation, it is not clear how we should interpret the retrieved data for the button case. + // Experiments made with SpaceNavigator: + // retrieved_data[1] == 0 if no button pressed + // retrieved_data[1] == 1 if left button pressed + // retrieved_data[1] == 2 if right button pressed + // retrieved_data[1] == 3 if left and right button pressed + // seems to show that each button is associated to a bit of retrieved_data[1], which means that at max 8 buttons can be supported. + for (unsigned int i = 0; i < 8; ++i) { if (retrieved_data[1] & (0x1 << i)) m_state.set_button(i); } +// // On the other hand, other libraries, as in https://github.com/koenieee/CrossplatformSpacemouseDriver/blob/master/SpaceMouseDriver/driver/SpaceMouseController.cpp +// // interpret retrieved_data[1] as the button id +// if (retrieved_data[1] > 0) +// m_state.set_button((unsigned int)retrieved_data[1]); + break; } default: diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index 778a3efed..55deaf118 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -66,6 +66,7 @@ class Mouse3DController GLCanvas3D* m_canvas; mutable std::mutex m_mutex; hid_device* m_device; + std::string m_device_str; bool m_running; bool m_settings_dialog; From 587effbedf754855134c52bb4aa1c6fb447ebb61 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 3 Oct 2019 11:38:31 +0200 Subject: [PATCH 09/67] ENABLE_3DCONNEXION_DEVICES -> Refactored Mouse3DController to be unaware of current active GLCanvas3D --- src/slic3r/GUI/Camera.cpp | 20 ++++++++++++++++ src/slic3r/GUI/Camera.hpp | 5 ++++ src/slic3r/GUI/GLCanvas3D.cpp | 36 ++++++++++++++++++++++++++-- src/slic3r/GUI/GLCanvas3D.hpp | 5 ++++ src/slic3r/GUI/MainFrame.cpp | 4 ---- src/slic3r/GUI/Mouse3DController.cpp | 19 ++++++--------- src/slic3r/GUI/Mouse3DController.hpp | 20 ++++------------ src/slic3r/GUI/Plater.cpp | 8 ------- 8 files changed, 75 insertions(+), 42 deletions(-) diff --git a/src/slic3r/GUI/Camera.cpp b/src/slic3r/GUI/Camera.cpp index 8e3a6d1f1..4bb3e6ad5 100644 --- a/src/slic3r/GUI/Camera.cpp +++ b/src/slic3r/GUI/Camera.cpp @@ -103,6 +103,25 @@ void Camera::set_theta(float theta, bool apply_limit) } } +#if ENABLE_3DCONNEXION_DEVICES +void Camera::update_zoom(double delta_zoom) +{ + set_zoom(m_zoom / (1.0 - std::max(std::min(delta_zoom, 4.0), -4.0) * 0.1)); +} + +void Camera::set_zoom(double zoom) +{ + // Don't allow to zoom too far outside the scene. + double zoom_min = calc_zoom_to_bounding_box_factor(m_scene_box, (int)m_viewport[2], (int)m_viewport[3]); + if (zoom_min > 0.0) + zoom = std::max(zoom, zoom_min * 0.7); + + // Don't allow to zoom too close to the scene. + zoom = std::min(zoom, 100.0); + + m_zoom = zoom; +} +#else void Camera::set_zoom(double zoom, const BoundingBoxf3& max_box, int canvas_w, int canvas_h) { zoom = std::max(std::min(zoom, 4.0), -4.0) / 10.0; @@ -118,6 +137,7 @@ void Camera::set_zoom(double zoom, const BoundingBoxf3& max_box, int canvas_w, i m_zoom = zoom; } +#endif // ENABLE_3DCONNEXION_DEVICES bool Camera::select_view(const std::string& direction) { diff --git a/src/slic3r/GUI/Camera.hpp b/src/slic3r/GUI/Camera.hpp index 839d0d6cf..bf452833c 100644 --- a/src/slic3r/GUI/Camera.hpp +++ b/src/slic3r/GUI/Camera.hpp @@ -63,8 +63,13 @@ public: void set_theta(float theta, bool apply_limit); double get_zoom() const { return m_zoom; } +#if ENABLE_3DCONNEXION_DEVICES + void update_zoom(double delta_zoom); + void set_zoom(double zoom); +#else void set_zoom(double zoom, const BoundingBoxf3& max_box, int canvas_w, int canvas_h); void set_zoom(double zoom) { m_zoom = zoom; } +#endif // ENABLE_3DCONNEXION_DEVICES const BoundingBoxf3& get_scene_box() const { return m_scene_box; } void set_scene_box(const BoundingBoxf3& box) { m_scene_box = box; } diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 562a939fb..6f94647ac 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1394,7 +1394,11 @@ BoundingBoxf3 GLCanvas3D::volumes_bounding_box() const BoundingBoxf3 GLCanvas3D::scene_bounding_box() const { BoundingBoxf3 bb = volumes_bounding_box(); +#if ENABLE_3DCONNEXION_DEVICES + bb.merge(m_bed.get_bounding_box(true)); +#else bb.merge(m_bed.get_bounding_box(false)); +#endif // ENABLE_3DCONNEXION_DEVICES if (m_config != nullptr) { @@ -1542,10 +1546,16 @@ void GLCanvas3D::render() return; } +#if ENABLE_3DCONNEXION_DEVICES + const Size& cnv_size = get_canvas_size(); +#endif // ENABLE_3DCONNEXION_DEVICES + if (m_camera.requires_zoom_to_bed) { zoom_to_bed(); +#if !ENABLE_3DCONNEXION_DEVICES const Size& cnv_size = get_canvas_size(); +#endif // !ENABLE_3DCONNEXION_DEVICES _resize((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height()); m_camera.requires_zoom_to_bed = false; } @@ -1637,7 +1647,7 @@ void GLCanvas3D::render() #endif // ENABLE_CAMERA_STATISTICS #if ENABLE_3DCONNEXION_DEVICES - wxGetApp().plater()->get_mouse3d_controller().render_settings_dialog(); + wxGetApp().plater()->get_mouse3d_controller().render_settings_dialog((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height()); #endif // ENABLE_3DCONNEXION_DEVICES wxGetApp().imgui()->render(); @@ -2313,7 +2323,7 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt) m_dirty |= m_undoredo_toolbar.update_items_state(); m_dirty |= m_view_toolbar.update_items_state(); #if ENABLE_3DCONNEXION_DEVICES - bool mouse3d_controller_applied = wxGetApp().plater()->get_mouse3d_controller().apply(); + bool mouse3d_controller_applied = wxGetApp().plater()->get_mouse3d_controller().apply(m_camera); m_dirty |= mouse3d_controller_applied; #endif // ENABLE_3DCONNEXION_DEVICES @@ -2460,11 +2470,19 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) case 'B': case 'b': { zoom_to_bed(); break; } case 'I': +#if ENABLE_3DCONNEXION_DEVICES + case 'i': { _update_camera_zoom(1.0); break; } +#else case 'i': { set_camera_zoom(1.0); break; } +#endif // ENABLE_3DCONNEXION_DEVICES case 'K': case 'k': { m_camera.select_next_type(); m_dirty = true; break; } case 'O': +#if ENABLE_3DCONNEXION_DEVICES + case 'o': { _update_camera_zoom(-1.0); break; } +#else case 'o': { set_camera_zoom(-1.0); break; } +#endif // ENABLE_3DCONNEXION_DEVICES #if ENABLE_RENDER_PICKING_PASS case 'T': case 't': { @@ -2618,7 +2636,11 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt) return; // Calculate the zoom delta and apply it to the current zoom factor +#if ENABLE_3DCONNEXION_DEVICES + _update_camera_zoom((double)evt.GetWheelRotation() / (double)evt.GetWheelDelta()); +#else set_camera_zoom((double)evt.GetWheelRotation() / (double)evt.GetWheelDelta()); +#endif // ENABLE_3DCONNEXION_DEVICES } void GLCanvas3D::on_timer(wxTimerEvent& evt) @@ -3413,12 +3435,14 @@ void GLCanvas3D::do_mirror(const std::string& snapshot_type) m_dirty = true; } +#if !ENABLE_3DCONNEXION_DEVICES void GLCanvas3D::set_camera_zoom(double zoom) { const Size& cnv_size = get_canvas_size(); m_camera.set_zoom(zoom, _max_bounding_box(false, true), cnv_size.get_width(), cnv_size.get_height()); m_dirty = true; } +#endif // !ENABLE_3DCONNEXION_DEVICES void GLCanvas3D::update_gizmos_on_off_state() { @@ -3887,6 +3911,14 @@ void GLCanvas3D::_zoom_to_box(const BoundingBoxf3& box) m_dirty = true; } +#if ENABLE_3DCONNEXION_DEVICES +void GLCanvas3D::_update_camera_zoom(double zoom) +{ + m_camera.update_zoom(zoom); + m_dirty = true; +} +#endif // ENABLE_3DCONNEXION_DEVICES + void GLCanvas3D::_refresh_if_shown_on_screen() { if (_is_shown_on_screen()) diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index cce6b0f2b..3051ae653 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -567,7 +567,9 @@ public: void do_flatten(const Vec3d& normal, const std::string& snapshot_type); void do_mirror(const std::string& snapshot_type); +#if !ENABLE_3DCONNEXION_DEVICES void set_camera_zoom(double zoom); +#endif // !ENABLE_3DCONNEXION_DEVICES void update_gizmos_on_off_state(); void reset_all_gizmos() { m_gizmos.reset_all_states(); } @@ -640,6 +642,9 @@ private: BoundingBoxf3 _max_bounding_box(bool include_gizmos, bool include_bed_model) const; void _zoom_to_box(const BoundingBoxf3& box); +#if ENABLE_3DCONNEXION_DEVICES + void _update_camera_zoom(double zoom); +#endif // ENABLE_3DCONNEXION_DEVICES void _refresh_if_shown_on_screen(); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 615d7d8a9..eaf6944b6 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -111,10 +111,6 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S } if(m_plater) m_plater->stop_jobs(); -#if ENABLE_3DCONNEXION_DEVICES - if (m_plater != nullptr) - m_plater->get_mouse3d_controller().set_canvas(nullptr); -#endif // ENABLE_3DCONNEXION_DEVICES // Weird things happen as the Paint messages are floating around the windows being destructed. // Avoid the Paint messages by hiding the main window. diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index c7bf89b4d..ccf1adfa2 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -3,7 +3,7 @@ #if ENABLE_3DCONNEXION_DEVICES -#include "GLCanvas3D.hpp" +#include "Camera.hpp" #include "GUI_App.hpp" #include "PresetBundle.hpp" #include "AppConfig.hpp" @@ -115,13 +115,12 @@ bool Mouse3DController::State::has_any_button() const return !m_buttons.empty(); } -bool Mouse3DController::State::apply(GLCanvas3D& canvas) +bool Mouse3DController::State::apply(Camera& camera) { if (!wxGetApp().IsActive()) return false; bool ret = false; - Camera& camera = canvas.get_camera(); if (has_translation()) { @@ -150,8 +149,8 @@ bool Mouse3DController::State::apply(GLCanvas3D& canvas) { switch (i) { - case 0: { canvas.set_camera_zoom(1.0); break; } - case 1: { canvas.set_camera_zoom(-1.0); break; } + case 0: { camera.update_zoom(1.0); break; } + case 1: { camera.update_zoom(-1.0); break; } default: { break; } } } @@ -165,7 +164,6 @@ bool Mouse3DController::State::apply(GLCanvas3D& canvas) Mouse3DController::Mouse3DController() : m_initialized(false) - , m_canvas(nullptr) , m_device(nullptr) , m_device_str("") , m_running(false) @@ -206,17 +204,14 @@ void Mouse3DController::shutdown() m_initialized = false; } -void Mouse3DController::render_settings_dialog() const +void Mouse3DController::render_settings_dialog(unsigned int canvas_width, unsigned int canvas_height) const { - if ((m_canvas == nullptr) || !m_running || !m_settings_dialog) + if (!m_running || !m_settings_dialog) return; ImGuiWrapper& imgui = *wxGetApp().imgui(); - std::lock_guard lock(m_mutex); - Size cnv_size = m_canvas->get_canvas_size(); - - imgui.set_next_window_pos(0.5f * (float)cnv_size.get_width(), 0.5f * (float)cnv_size.get_height(), ImGuiCond_Always, 0.5f, 0.5f); + imgui.set_next_window_pos(0.5f * (float)canvas_width, 0.5f * (float)canvas_height, ImGuiCond_Always, 0.5f, 0.5f); imgui.set_next_window_bg_alpha(0.5f); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index 55deaf118..bfad1b3b2 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -13,7 +13,7 @@ namespace Slic3r { namespace GUI { -class GLCanvas3D; +struct Camera; class Mouse3DController { @@ -57,14 +57,12 @@ class Mouse3DController void set_rotation_scale(float scale) { m_rotation_scale = scale; } // return true if any change to the camera took place - bool apply(GLCanvas3D& canvas); + bool apply(Camera& camera); }; bool m_initialized; mutable State m_state; std::thread m_thread; - GLCanvas3D* m_canvas; - mutable std::mutex m_mutex; hid_device* m_device; std::string m_device_str; bool m_running; @@ -79,26 +77,16 @@ public: bool is_device_connected() const { return m_device != nullptr; } bool is_running() const { return m_running; } - void set_canvas(GLCanvas3D* canvas) - { - std::lock_guard lock(m_mutex); - m_canvas = canvas; - } - bool has_translation() const { return m_state.has_translation(); } bool has_rotation() const { return m_state.has_rotation(); } bool has_translation_or_rotation() const { return m_state.has_translation_or_rotation(); } bool has_any_button() const { return m_state.has_any_button(); } - bool apply() - { - std::lock_guard lock(m_mutex); - return (m_canvas != nullptr) ? m_state.apply(*m_canvas) : false; - } + bool apply(Camera& camera) { return m_state.apply(camera); } bool is_settings_dialog_shown() const { return m_settings_dialog; } void show_settings_dialog(bool show) { m_settings_dialog = show; } - void render_settings_dialog() const; + void render_settings_dialog(unsigned int canvas_width, unsigned int canvas_height) const; private: void connect_device(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index c3214336b..b85da3373 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3263,10 +3263,6 @@ void Plater::priv::set_current_panel(wxPanel* panel) view3D->reload_scene(true); } -#if ENABLE_3DCONNEXION_DEVICES - mouse3d_controller.set_canvas(view3D->get_canvas3d()); -#endif // ENABLE_3DCONNEXION_DEVICES - // sets the canvas as dirty to force a render at the 1st idle event (wxWidgets IsShownOnScreen() is buggy and cannot be used reliably) view3D->set_as_dirty(); view_toolbar.select_item("3D"); @@ -3282,10 +3278,6 @@ void Plater::priv::set_current_panel(wxPanel* panel) // keeps current gcode preview, if any preview->reload_print(true); -#if ENABLE_3DCONNEXION_DEVICES - mouse3d_controller.set_canvas(preview->get_canvas3d()); -#endif // ENABLE_3DCONNEXION_DEVICES - preview->set_canvas_as_dirty(); view_toolbar.select_item("Preview"); } From b15757a126f23bae68221243c94faf62a0134f47 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 3 Oct 2019 12:09:49 +0200 Subject: [PATCH 10/67] ENABLE_3DCONNEXION_DEVICES - Added missing return lines --- src/slic3r/GUI/AppConfig.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/slic3r/GUI/AppConfig.cpp b/src/slic3r/GUI/AppConfig.cpp index 2d5ee6f5f..6d1c9cddd 100644 --- a/src/slic3r/GUI/AppConfig.cpp +++ b/src/slic3r/GUI/AppConfig.cpp @@ -293,6 +293,7 @@ bool AppConfig::get_mouse_device_translation_speed(const std::string& name, doub return false; translation_speed = ::atof(it_val->second.c_str()); + return true; } bool AppConfig::get_mouse_device_rotation_speed(const std::string& name, float& rotation_speed) @@ -307,6 +308,7 @@ bool AppConfig::get_mouse_device_rotation_speed(const std::string& name, float& return false; rotation_speed = (float)::atof(it_val->second.c_str()); + return true; } #endif // ENABLE_3DCONNEXION_DEVICES From f31568180465b8c8662a2569942998f697d281b2 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 3 Oct 2019 12:16:59 +0200 Subject: [PATCH 11/67] ENABLE_3DCONNEXION_DEVICES -> Refactored Mouse3DController to simplify code --- src/slic3r/GUI/Mouse3DController.cpp | 89 +++++----------------------- src/slic3r/GUI/Mouse3DController.hpp | 27 ++++----- 2 files changed, 27 insertions(+), 89 deletions(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index ccf1adfa2..f4e57b372 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -49,72 +49,6 @@ Mouse3DController::State::State() { } -void Mouse3DController::State::set_translation(const Vec3d& translation) -{ - std::lock_guard lock(m_mutex); - m_translation = translation; -} - -void Mouse3DController::State::set_rotation(const Vec3f& rotation) -{ - std::lock_guard lock(m_mutex); - m_rotation = rotation; -} - -void Mouse3DController::State::set_button(unsigned int id) -{ - std::lock_guard lock(m_mutex); - m_buttons.push_back(id); -} - -void Mouse3DController::State::reset_buttons() -{ - std::lock_guard lock(m_mutex); - return m_buttons.clear(); -} - -const Vec3d& Mouse3DController::State::get_translation() const -{ - std::lock_guard lock(m_mutex); - return m_translation; -} - -const Vec3f& Mouse3DController::State::get_rotation() const -{ - std::lock_guard lock(m_mutex); - return m_rotation; -} - -const std::vector& Mouse3DController::State::get_buttons() const -{ - std::lock_guard lock(m_mutex); - return m_buttons; -} - -bool Mouse3DController::State::has_translation() const -{ - std::lock_guard lock(m_mutex); - return !m_translation.isApprox(Vec3d::Zero()); -} - -bool Mouse3DController::State::has_rotation() const -{ - std::lock_guard lock(m_mutex); - return !m_rotation.isApprox(Vec3f::Zero()); -} - -bool Mouse3DController::State::has_translation_or_rotation() const -{ - std::lock_guard lock(m_mutex); - return !m_translation.isApprox(Vec3d::Zero()) && !m_rotation.isApprox(Vec3f::Zero()); -} - -bool Mouse3DController::State::has_any_button() const -{ - std::lock_guard lock(m_mutex); - return !m_buttons.empty(); -} - bool Mouse3DController::State::apply(Camera& camera) { if (!wxGetApp().IsActive()) @@ -124,28 +58,25 @@ bool Mouse3DController::State::apply(Camera& camera) if (has_translation()) { - Vec3d translation = get_translation(); - camera.set_target(camera.get_target() + m_translation_scale * (translation(0) * camera.get_dir_right() + translation(1) * camera.get_dir_forward() + translation(2) * camera.get_dir_up())); - set_translation(Vec3d::Zero()); + camera.set_target(camera.get_target() + m_translation_scale * (m_translation(0) * camera.get_dir_right() + m_translation(1) * camera.get_dir_forward() + m_translation(2) * camera.get_dir_up())); + m_translation = Vec3d::Zero(); ret = true; } if (has_rotation()) { - Vec3f rotation = get_rotation(); - float theta = m_rotation_scale * rotation(0); - float phi = m_rotation_scale * rotation(2); + float theta = m_rotation_scale * m_rotation(0); + float phi = m_rotation_scale * m_rotation(2); float sign = camera.inverted_phi ? -1.0f : 1.0f; camera.phi += sign * phi; camera.set_theta(camera.get_theta() + theta, wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA); - set_rotation(Vec3f::Zero()); + m_rotation = Vec3f::Zero(); ret = true; } if (has_any_button()) { - std::vector buttons = get_buttons(); - for (unsigned int i : buttons) + for (unsigned int i : m_buttons) { switch (i) { @@ -204,6 +135,12 @@ void Mouse3DController::shutdown() m_initialized = false; } +bool Mouse3DController::apply(Camera& camera) +{ + std::lock_guard lock(m_mutex); + return m_state.apply(camera); +} + void Mouse3DController::render_settings_dialog(unsigned int canvas_width, unsigned int canvas_height) const { if (!m_running || !m_settings_dialog) @@ -383,6 +320,8 @@ void Mouse3DController::collect_input() return; } + std::lock_guard lock(m_mutex); + if (res > 0) { switch (retrieved_data[0]) diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index bfad1b3b2..8abc4299c 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -24,8 +24,6 @@ class Mouse3DController static const float DefaultRotationScale; private: - mutable std::mutex m_mutex; - Vec3d m_translation; Vec3f m_rotation; std::vector m_buttons; @@ -36,19 +34,19 @@ class Mouse3DController public: State(); - void set_translation(const Vec3d& translation); - void set_rotation(const Vec3f& rotation); - void set_button(unsigned int id); - void reset_buttons(); + void set_translation(const Vec3d& translation) { m_translation = translation; } + void set_rotation(const Vec3f& rotation) { m_rotation = rotation; } + void set_button(unsigned int id) { m_buttons.push_back(id); } + void reset_buttons() { return m_buttons.clear(); } - const Vec3d& get_translation() const; - const Vec3f& get_rotation() const; - const std::vector& get_buttons() const; + const Vec3d& get_translation() const { return m_translation; } + const Vec3f& get_rotation() const { return m_rotation; } + const std::vector& get_buttons() const { return m_buttons; } - bool has_translation() const; - bool has_rotation() const; - bool has_translation_or_rotation() const; - bool has_any_button() const; + bool has_translation() const { return !m_translation.isApprox(Vec3d::Zero()); } + bool has_rotation() const { return !m_rotation.isApprox(Vec3f::Zero()); } + bool has_translation_or_rotation() const { return !m_translation.isApprox(Vec3d::Zero()) && !m_rotation.isApprox(Vec3f::Zero()); } + bool has_any_button() const { return !m_buttons.empty(); } double get_translation_scale() const { return m_translation_scale; } void set_translation_scale(double scale) { m_translation_scale = scale; } @@ -62,6 +60,7 @@ class Mouse3DController bool m_initialized; mutable State m_state; + std::mutex m_mutex; std::thread m_thread; hid_device* m_device; std::string m_device_str; @@ -82,7 +81,7 @@ public: bool has_translation_or_rotation() const { return m_state.has_translation_or_rotation(); } bool has_any_button() const { return m_state.has_any_button(); } - bool apply(Camera& camera) { return m_state.apply(camera); } + bool apply(Camera& camera); bool is_settings_dialog_shown() const { return m_settings_dialog; } void show_settings_dialog(bool show) { m_settings_dialog = show; } From 1d463fc23f9bcd2d24a26a00f845ca42c3aa3a6c Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 4 Oct 2019 07:58:01 +0200 Subject: [PATCH 12/67] ENABLE_3DCONNEXION_DEVICES -> Another small refactoring of Mouse3DController --- src/slic3r/GUI/Mouse3DController.cpp | 44 +++++++++++++--------------- src/slic3r/GUI/Mouse3DController.hpp | 4 --- 2 files changed, 21 insertions(+), 27 deletions(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index f4e57b372..660d8d809 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -285,24 +285,22 @@ void Mouse3DController::run() } } -double convert_input(int first, unsigned char val) -{ - int ret = 0; - - switch (val) - { - case 0: { ret = first; break; } - case 1: { ret = first + 255; break; } - case 254: { ret = -511 + first; break; } - case 255: { ret = -255 + first; break; } - default: { break; } - } - - return (double)ret / 349.0; -} - void Mouse3DController::collect_input() { + auto convert_input = [](unsigned char first, unsigned char second)-> double + { + int ret = 0; + switch (second) + { + case 0: { ret = (int)first; break; } + case 1: { ret = (int)first + 255; break; } + case 254: { ret = -511 + (int)first; break; } + case 255: { ret = -255 + (int)first; break; } + default: { break; } + } + return (double)ret / 349.0; + }; + // Read data from device enum EDataType { @@ -328,19 +326,19 @@ void Mouse3DController::collect_input() { case Translation: { - Vec3d translation(-convert_input((int)retrieved_data[1], retrieved_data[2]), - convert_input((int)retrieved_data[3], retrieved_data[4]), - convert_input((int)retrieved_data[5], retrieved_data[6])); - if (!translation.isApprox(Vec3d::Zero())) + Vec3d translation(-convert_input(retrieved_data[1], retrieved_data[2]), + convert_input(retrieved_data[3], retrieved_data[4]), + convert_input(retrieved_data[5], retrieved_data[6])); + if (!translation.isApprox(Vec3d::Zero())) m_state.set_translation(translation); break; } case Rotation: { - Vec3f rotation(-(float)convert_input((int)retrieved_data[1], retrieved_data[2]), - (float)convert_input((int)retrieved_data[3], retrieved_data[4]), - -(float)convert_input((int)retrieved_data[5], retrieved_data[6])); + Vec3f rotation(-(float)convert_input(retrieved_data[1], retrieved_data[2]), + (float)convert_input(retrieved_data[3], retrieved_data[4]), + -(float)convert_input(retrieved_data[5], retrieved_data[6])); if (!rotation.isApprox(Vec3f::Zero())) m_state.set_rotation(rotation); diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index 8abc4299c..bb15d4351 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -39,10 +39,6 @@ class Mouse3DController void set_button(unsigned int id) { m_buttons.push_back(id); } void reset_buttons() { return m_buttons.clear(); } - const Vec3d& get_translation() const { return m_translation; } - const Vec3f& get_rotation() const { return m_rotation; } - const std::vector& get_buttons() const { return m_buttons; } - bool has_translation() const { return !m_translation.isApprox(Vec3d::Zero()); } bool has_rotation() const { return !m_rotation.isApprox(Vec3f::Zero()); } bool has_translation_or_rotation() const { return !m_translation.isApprox(Vec3d::Zero()) && !m_rotation.isApprox(Vec3f::Zero()); } From 5a94ac94999d2f85f8103b3ed2b7162c35a9975f Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 4 Oct 2019 10:59:27 +0200 Subject: [PATCH 13/67] ENABLE_3DCONNEXION_DEVICES -> Automatic detection of plugging/unplugging 3Dconnexion devices --- src/slic3r/GUI/Mouse3DController.cpp | 61 ++++++++++++++++++++-------- src/slic3r/GUI/Mouse3DController.hpp | 11 ++--- 2 files changed, 50 insertions(+), 22 deletions(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 660d8d809..d7e9bda4a 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -11,6 +11,7 @@ #include #include +#include #include "I18N.hpp" @@ -110,12 +111,12 @@ void Mouse3DController::init() // Initialize the hidapi library int res = hid_init(); if (res != 0) + { + BOOST_LOG_TRIVIAL(error) << "Unable to initialize hidapi library"; return; + } m_initialized = true; - - connect_device(); - start(); } void Mouse3DController::shutdown() @@ -124,10 +125,6 @@ void Mouse3DController::shutdown() return; stop(); - - if (m_thread.joinable()) - m_thread.join(); - disconnect_device(); // Finalize the hidapi library @@ -137,8 +134,24 @@ void Mouse3DController::shutdown() bool Mouse3DController::apply(Camera& camera) { + if (!m_initialized) + return false; + std::lock_guard lock(m_mutex); - return m_state.apply(camera); + + // check if the user unplugged the device + if (!m_running && is_device_connected()) + { + disconnect_device(); + // hides the settings dialog if the user re-plug the device + m_settings_dialog = false; + } + + // check if the user plugged the device + if (connect_device()) + start(); + + return is_device_connected() ? m_state.apply(camera) : false; } void Mouse3DController::render_settings_dialog(unsigned int canvas_width, unsigned int canvas_height) const @@ -180,15 +193,18 @@ void Mouse3DController::render_settings_dialog(unsigned int canvas_width, unsign ImGui::PopStyleVar(); } -void Mouse3DController::connect_device() +bool Mouse3DController::connect_device() { - if (m_device != nullptr) - return; + if (is_device_connected()) + return false; // Enumerates devices hid_device_info* devices = hid_enumerate(0, 0); if (devices == nullptr) - return; + { + BOOST_LOG_TRIVIAL(error) << "Unable to enumerate HID devices"; + return false; + } // Searches for 1st connected 3Dconnexion device unsigned short vendor_id = 0; @@ -231,7 +247,7 @@ void Mouse3DController::connect_device() hid_free_enumeration(devices); if (vendor_id == 0) - return; + return false; // Open the 3Dconnexion device using the VID, PID m_device = hid_open(vendor_id, product_id, nullptr); @@ -242,7 +258,9 @@ void Mouse3DController::connect_device() hid_get_product_string(m_device, product.data(), 1024); m_device_str = boost::nowide::narrow(product.data()); - // gets device parameters from the config, if present + BOOST_LOG_TRIVIAL(info) << "Connected device: " << m_device_str; + + // get device parameters from the config, if present double translation = 1.0; float rotation = 1.0; wxGetApp().app_config->get_mouse_device_translation_speed(m_device_str, translation); @@ -251,26 +269,35 @@ void Mouse3DController::connect_device() m_state.set_translation_scale(State::DefaultTranslationScale * std::max(0.5, std::min(2.0, translation))); m_state.set_rotation_scale(State::DefaultRotationScale * std::max(0.5f, std::min(2.0f, rotation))); } + + return (m_device != nullptr); } void Mouse3DController::disconnect_device() { - if (m_device == nullptr) + if (!is_device_connected()) return; - // stores current device parameters into the config + // Stop the secondary thread, if running + if (m_thread.joinable()) + m_thread.join(); + + // Store current device parameters into the config wxGetApp().app_config->set_mouse_device(m_device_str, m_state.get_translation_scale() / State::DefaultTranslationScale, m_state.get_rotation_scale() / State::DefaultRotationScale); wxGetApp().app_config->save(); // Close the 3Dconnexion device hid_close(m_device); m_device = nullptr; + + BOOST_LOG_TRIVIAL(info) << "Disconnected device: " << m_device_str; + m_device_str = ""; } void Mouse3DController::start() { - if ((m_device == nullptr) || m_running) + if (!is_device_connected() || m_running) return; m_thread = std::thread(&Mouse3DController::run, this); diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index bb15d4351..050ef436e 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -72,23 +72,24 @@ public: bool is_device_connected() const { return m_device != nullptr; } bool is_running() const { return m_running; } - bool has_translation() const { return m_state.has_translation(); } - bool has_rotation() const { return m_state.has_rotation(); } +// bool has_translation() const { return m_state.has_translation(); } +// bool has_rotation() const { return m_state.has_rotation(); } bool has_translation_or_rotation() const { return m_state.has_translation_or_rotation(); } - bool has_any_button() const { return m_state.has_any_button(); } +// bool has_any_button() const { return m_state.has_any_button(); } bool apply(Camera& camera); bool is_settings_dialog_shown() const { return m_settings_dialog; } - void show_settings_dialog(bool show) { m_settings_dialog = show; } + void show_settings_dialog(bool show) { m_settings_dialog = show && is_running(); } void render_settings_dialog(unsigned int canvas_width, unsigned int canvas_height) const; private: - void connect_device(); + bool connect_device(); void disconnect_device(); void start(); void stop() { m_running = false; } + // secondary thread methods void run(); void collect_input(); }; From 0ba9fbaf0c568f5f377bac6579ae693f856cfa7a Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 7 Oct 2019 09:31:23 +0200 Subject: [PATCH 14/67] ENABLE_3DCONNEXION_DEVICES -> Update 3D scene only when mouse 3D device send update messages --- src/slic3r/GUI/GLCanvas3D.cpp | 5 ++--- src/slic3r/GUI/Mouse3DController.cpp | 17 ++++++++++++++++- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 6f94647ac..7b2b5f6d4 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2323,8 +2323,7 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt) m_dirty |= m_undoredo_toolbar.update_items_state(); m_dirty |= m_view_toolbar.update_items_state(); #if ENABLE_3DCONNEXION_DEVICES - bool mouse3d_controller_applied = wxGetApp().plater()->get_mouse3d_controller().apply(m_camera); - m_dirty |= mouse3d_controller_applied; + m_dirty |= wxGetApp().plater()->get_mouse3d_controller().apply(m_camera); #endif // ENABLE_3DCONNEXION_DEVICES if (!m_dirty) @@ -2333,7 +2332,7 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt) _refresh_if_shown_on_screen(); #if ENABLE_3DCONNEXION_DEVICES - if (m_keep_dirty || wxGetApp().plater()->get_mouse3d_controller().is_device_connected()) + if (m_keep_dirty) { m_dirty = true; evt.RequestMore(); diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index d7e9bda4a..f32f54b2d 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -349,6 +349,8 @@ void Mouse3DController::collect_input() if (res > 0) { + bool updated = false; + switch (retrieved_data[0]) { case Translation: @@ -356,8 +358,11 @@ void Mouse3DController::collect_input() Vec3d translation(-convert_input(retrieved_data[1], retrieved_data[2]), convert_input(retrieved_data[3], retrieved_data[4]), convert_input(retrieved_data[5], retrieved_data[6])); - if (!translation.isApprox(Vec3d::Zero())) + if (!translation.isApprox(Vec3d::Zero())) + { + updated = true; m_state.set_translation(translation); + } break; } @@ -367,7 +372,10 @@ void Mouse3DController::collect_input() (float)convert_input(retrieved_data[3], retrieved_data[4]), -(float)convert_input(retrieved_data[5], retrieved_data[6])); if (!rotation.isApprox(Vec3f::Zero())) + { + updated = true; m_state.set_rotation(rotation); + } break; } @@ -383,7 +391,10 @@ void Mouse3DController::collect_input() for (unsigned int i = 0; i < 8; ++i) { if (retrieved_data[1] & (0x1 << i)) + { + updated = true; m_state.set_button(i); + } } // // On the other hand, other libraries, as in https://github.com/koenieee/CrossplatformSpacemouseDriver/blob/master/SpaceMouseDriver/driver/SpaceMouseController.cpp @@ -396,6 +407,10 @@ void Mouse3DController::collect_input() default: break; } + + if (updated) + // ask for an idle event to update 3D scene + wxWakeUpIdle(); } } From 8fcd4e4407a747d5ac5cf46cfcf6c027f9ff825b Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 8 Oct 2019 09:52:56 +0200 Subject: [PATCH 15/67] ENABLE_3DCONNEXION_DEVICES -> added debug code to get 3Cconnexion device product id --- src/slic3r/GUI/Mouse3DController.cpp | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 2bd6d9020..854d67862 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -26,21 +26,21 @@ static const std::vector _3DCONNEXION_VENDORS = static const std::vector _3DCONNEXION_DEVICES = { 0xC623, // TRAVELER = 50723 - 0xC626, // NAVIGATOR = 50726 + 0xC626, // NAVIGATOR = 50726 *TESTED* 0xc628, // NAVIGATOR_FOR_NOTEBOOKS = 50728 0xc627, // SPACEEXPLORER = 50727 0xC603, // SPACEMOUSE = 50691 0xC62B, // SPACEMOUSEPRO = 50731 0xc621, // SPACEBALL5000 = 50721 0xc625, // SPACEPILOT = 50725 - 0xc629 // SPACEPILOTPRO = 50729 + 0xc652, // SPACEMOUSE PRO WIRELESS = 50770 *TESTED* }; namespace Slic3r { namespace GUI { const double Mouse3DController::State::DefaultTranslationScale = 2.5; -const float Mouse3DController::State::DefaultRotationScale = 1.0; +const float Mouse3DController::State::DefaultRotationScale = 1.0f; Mouse3DController::State::State() : m_translation_scale(DefaultTranslationScale) @@ -189,8 +189,8 @@ void Mouse3DController::render_settings_dialog(unsigned int canvas_width, unsign ImGui::PopStyleColor(); ImGui::SameLine(); imgui.text(m_device_str); - ImGui::Separator(); + ImGui::Separator(); ImGui::PushStyleColor(ImGuiCol_Text, color); imgui.text(_(L("Speed:"))); ImGui::PopStyleColor(); @@ -199,7 +199,7 @@ void Mouse3DController::render_settings_dialog(unsigned int canvas_width, unsign if (ImGui::SliderFloat(_(L("Translation")), &translation, 0.5f, 2.0f, "%.1f")) m_state.set_translation_scale(State::DefaultTranslationScale * (double)translation); - float rotation = (float)m_state.get_rotation_scale() / State::DefaultRotationScale; + float rotation = m_state.get_rotation_scale() / State::DefaultRotationScale; if (ImGui::SliderFloat(_(L("Rotation")), &rotation, 0.5f, 2.0f, "%.1f")) m_state.set_rotation_scale(State::DefaultRotationScale * rotation); @@ -228,6 +228,17 @@ bool Mouse3DController::connect_device() hid_device_info* current = devices; while (current != nullptr) { +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + if (current->manufacturer_string != nullptr) + { + std::string aaa = boost::nowide::narrow(current->manufacturer_string); + if (aaa == "3Dconnexion") + { + std::cout << "Detected 3Dconnexion device code: " << current->product_id << " (" << std::hex << current->product_id << std::dec << ")" << std::endl; + } + } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + for (size_t i = 0; i < _3DCONNEXION_VENDORS.size(); ++i) { if (_3DCONNEXION_VENDORS[i] == current->vendor_id) From 9447d3e1b529b246fc8be183cc7cc9be929f4a71 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 8 Oct 2019 13:38:08 +0200 Subject: [PATCH 16/67] ENABLE_3DCONNEXION_DEVICES -> Mouse3DController reworked to handle reports of length 7 bytes and 13 bytes --- src/slic3r/GUI/Mouse3DController.cpp | 288 +++++++++++++++++---------- src/slic3r/GUI/Mouse3DController.hpp | 7 + 2 files changed, 188 insertions(+), 107 deletions(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 854d67862..c62b8c851 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -22,18 +22,30 @@ static const std::vector _3DCONNEXION_VENDORS = 0x256F // 3DCONNECTION = 9583 // 3Dconnexion }; -// See: https://github.com/FreeSpacenav/spacenavd/blob/a9eccf34e7cac969ee399f625aef827f4f4aaec6/src/dev.c#L202 for a wider list of devices +// See: https://github.com/FreeSpacenav/spacenavd/blob/a9eccf34e7cac969ee399f625aef827f4f4aaec6/src/dev.c#L202 static const std::vector _3DCONNEXION_DEVICES = { - 0xC623, // TRAVELER = 50723 - 0xC626, // NAVIGATOR = 50726 *TESTED* - 0xc628, // NAVIGATOR_FOR_NOTEBOOKS = 50728 - 0xc627, // SPACEEXPLORER = 50727 - 0xC603, // SPACEMOUSE = 50691 - 0xC62B, // SPACEMOUSEPRO = 50731 - 0xc621, // SPACEBALL5000 = 50721 - 0xc625, // SPACEPILOT = 50725 - 0xc652, // SPACEMOUSE PRO WIRELESS = 50770 *TESTED* + 0xc603, /* 50691 spacemouse plus XT */ + 0xc605, /* 50693 cadman */ + 0xc606, /* 50694 spacemouse classic */ + 0xc621, /* 50721 spaceball 5000 */ + 0xc623, /* 50723 space traveller */ + 0xc625, /* 50725 space pilot */ + 0xc626, /* 50726 space navigator *TESTED* */ + 0xc627, /* 50727 space explorer */ + 0xc628, /* 50728 space navigator for notebooks*/ + 0xc629, /* 50729 space pilot pro*/ + 0xc62b, /* 50731 space mouse pro*/ + 0xc62e, /* 50734 spacemouse wireless (USB cable) */ + 0xc62f, /* 50735 spacemouse wireless receiver */ + 0xc631, /* 50737 spacemouse pro wireless *TESTED* */ + 0xc632, /* 50738 spacemouse pro wireless receiver */ + 0xc633, /* 50739 spacemouse enterprise */ + 0xc635, /* 50741 spacemouse compact */ + 0xc636, /* 50742 spacemouse module */ + 0xc640, /* 50752 nulooq */ + +// 0xc652, /* 50770 3Dconnexion universal receiver */ }; namespace Slic3r { @@ -51,18 +63,18 @@ Mouse3DController::State::State() bool Mouse3DController::State::process_mouse_wheel() { - if (m_mouse_wheel_counter > 0) + if (m_mouse_wheel_counter == 0) + return false; + else if (!m_rotation.empty()) { --m_mouse_wheel_counter; return true; } - else if (m_rotation.empty()) + else { m_mouse_wheel_counter = 0; return true; } - - return false; } bool Mouse3DController::State::apply(Camera& camera) @@ -225,20 +237,29 @@ bool Mouse3DController::connect_device() unsigned short vendor_id = 0; unsigned short product_id = 0; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + hid_device_info* cur = devices; + while (cur != nullptr) + { + std::cout << "Detected device '"; + + if (cur->manufacturer_string != nullptr) + std::wcout << cur->manufacturer_string << L" "; + else + std::wcout << "MMM "; + if (cur->product_string != nullptr) + std::wcout << cur->product_string; + else + std::wcout << "PPP"; + std::cout << "' code: " << cur->product_id << " (" << std::hex << cur->product_id << std::dec << ")" << std::endl; + + cur = cur->next; + } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + hid_device_info* current = devices; while (current != nullptr) { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - if (current->manufacturer_string != nullptr) - { - std::string aaa = boost::nowide::narrow(current->manufacturer_string); - if (aaa == "3Dconnexion") - { - std::cout << "Detected 3Dconnexion device code: " << current->product_id << " (" << std::hex << current->product_id << std::dec << ")" << std::endl; - } - } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - for (size_t i = 0; i < _3DCONNEXION_VENDORS.size(); ++i) { if (_3DCONNEXION_VENDORS[i] == current->vendor_id) @@ -340,30 +361,8 @@ void Mouse3DController::run() void Mouse3DController::collect_input() { - auto convert_input = [](unsigned char first, unsigned char second)-> double - { - int ret = 0; - switch (second) - { - case 0: { ret = (int)first; break; } - case 1: { ret = (int)first + 255; break; } - case 254: { ret = -511 + (int)first; break; } - case 255: { ret = -255 + (int)first; break; } - default: { break; } - } - return (double)ret / 349.0; - }; - - // Read data from device - enum EDataType - { - Translation = 1, - Rotation, - Button - }; - - unsigned char retrieved_data[8] = { 0 }; - int res = hid_read_timeout(m_device, retrieved_data, sizeof(retrieved_data), 100); + DataPacket packet = { 0 }; + int res = hid_read_timeout(m_device, packet.data(), packet.size(), 100); if (res < 0) { // An error occourred (device detached from pc ?) @@ -371,73 +370,148 @@ void Mouse3DController::collect_input() return; } + if (!wxGetApp().IsActive()) + return; + std::lock_guard lock(m_mutex); - if (res > 0) + bool updated = false; + + if (res == 7) + updated = handle_packet(packet); + else if (res == 13) + updated = handle_wireless_packet(packet); + else if (res > 0) + std::cout << "Got unknown data packet of length: " << res << ", code:" << (int)packet[0] << std::endl; + + if (updated) + // ask for an idle event to update 3D scene + wxWakeUpIdle(); +} + +bool Mouse3DController::handle_packet(const DataPacket& packet) +{ + switch (packet[0]) { - bool updated = false; - - switch (retrieved_data[0]) + case 1: // Translation { - case Translation: - { - Vec3d translation(-convert_input(retrieved_data[1], retrieved_data[2]), - convert_input(retrieved_data[3], retrieved_data[4]), - convert_input(retrieved_data[5], retrieved_data[6])); - if (!translation.isApprox(Vec3d::Zero())) - { - updated = true; - m_state.append_translation(translation); - } + if (handle_packet_translation(packet)) + return true; - break; - } - case Rotation: - { - Vec3f rotation(-(float)convert_input(retrieved_data[1], retrieved_data[2]), - (float)convert_input(retrieved_data[3], retrieved_data[4]), - -(float)convert_input(retrieved_data[5], retrieved_data[6])); - if (!rotation.isApprox(Vec3f::Zero())) - { - updated = true; - m_state.append_rotation(rotation); - } - - break; - } - case Button: - { - // Because of lack of documentation, it is not clear how we should interpret the retrieved data for the button case. - // Experiments made with SpaceNavigator: - // retrieved_data[1] == 0 if no button pressed - // retrieved_data[1] == 1 if left button pressed - // retrieved_data[1] == 2 if right button pressed - // retrieved_data[1] == 3 if left and right button pressed - // seems to show that each button is associated to a bit of retrieved_data[1], which means that at max 8 buttons can be supported. - for (unsigned int i = 0; i < 8; ++i) - { - if (retrieved_data[1] & (0x1 << i)) - { - updated = true; - m_state.append_button(i); - } - } - -// // On the other hand, other libraries, as in https://github.com/koenieee/CrossplatformSpacemouseDriver/blob/master/SpaceMouseDriver/driver/SpaceMouseController.cpp -// // interpret retrieved_data[1] as the button id -// if (retrieved_data[1] > 0) -// m_state.set_button((unsigned int)retrieved_data[1]); - - break; - } - default: break; } + case 2: // Rotation + { + if (handle_packet_rotation(packet, 1)) + return true; - if (updated) - // ask for an idle event to update 3D scene - wxWakeUpIdle(); + break; + } + case 3: // Button + { + if (handle_packet_button(packet, 6)) + return true; + + break; + } + default: + { + std::cout << "Got unknown data packet of code: " << (int)packet[0] << std::endl; + break; + } } + + return false; +} + +bool Mouse3DController::handle_wireless_packet(const DataPacket& packet) +{ + switch (packet[0]) + { + case 1: // Translation + Rotation + { + bool updated = handle_packet_translation(packet); + updated |= handle_packet_rotation(packet, 7); + + if (updated) + return true; + + break; + } + case 3: // Button + { + if (handle_packet_button(packet, 12)) + return true; + + break; + } + default: + { + std::cout << "Got unknown data packet of code: " << (int)packet[0] << std::endl; + break; + } + } + + return false; +} + +double convert_input(unsigned char first, unsigned char second) +{ + int ret = 0; + switch (second) + { + case 0: { ret = (int)first; break; } + case 1: { ret = (int)first + 255; break; } + case 254: { ret = -511 + (int)first; break; } + case 255: { ret = -255 + (int)first; break; } + default: { break; } + } + return (double)ret / 349.0; +}; + +bool Mouse3DController::handle_packet_translation(const DataPacket& packet) +{ + Vec3d translation(-convert_input(packet[1], packet[2]), + convert_input(packet[3], packet[4]), + convert_input(packet[5], packet[6])); + if (!translation.isApprox(Vec3d::Zero())) + { + m_state.append_translation(translation); + return true; + } + + return false; +} + +bool Mouse3DController::handle_packet_rotation(const DataPacket& packet, unsigned int first_byte) +{ + Vec3f rotation(-(float)convert_input(packet[first_byte + 0], packet[first_byte + 1]), + (float)convert_input(packet[first_byte + 2], packet[first_byte + 3]), + -(float)convert_input(packet[first_byte + 4], packet[first_byte + 5])); + if (!rotation.isApprox(Vec3f::Zero())) + { + m_state.append_rotation(rotation); + return true; + } + + return false; +} + +bool Mouse3DController::handle_packet_button(const DataPacket& packet, unsigned int packet_size) +{ + for (unsigned int i = 0, base = 0; i < packet_size; ++i, base += 8) + { + for (unsigned int j = 0; j < 8; ++j) + { + if (packet[i + 1] & (0x1 << j)) + { + m_state.append_button(base + j); + return true; + } + } + } + + return false; } } // namespace GUI diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index a894e312b..d7f3ed9f7 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -104,6 +104,13 @@ private: // secondary thread methods void run(); void collect_input(); + + typedef std::array DataPacket; + bool handle_packet(const DataPacket& packet); + bool handle_wireless_packet(const DataPacket& packet); + bool handle_packet_translation(const DataPacket& packet); + bool handle_packet_rotation(const DataPacket& packet, unsigned int first_byte); + bool handle_packet_button(const DataPacket& packet, unsigned int packet_size); }; } // namespace GUI From 4ec6199ef103ff391d81c0a5c5278719eee4ac40 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 8 Oct 2019 14:32:05 +0200 Subject: [PATCH 17/67] ENABLE_3DCONNEXION_DEVICES -> Added translation and rotation customizable parameter deadzone --- src/slic3r/GUI/AppConfig.cpp | 42 +++++++++++++++-- src/slic3r/GUI/AppConfig.hpp | 8 ++-- src/slic3r/GUI/GLCanvas3D.cpp | 1 + src/slic3r/GUI/Mouse3DController.cpp | 68 ++++++++++++++++++++-------- src/slic3r/GUI/Mouse3DController.hpp | 31 ++++++++++--- 5 files changed, 116 insertions(+), 34 deletions(-) diff --git a/src/slic3r/GUI/AppConfig.cpp b/src/slic3r/GUI/AppConfig.cpp index 6d0c83393..c29fe1c3b 100644 --- a/src/slic3r/GUI/AppConfig.cpp +++ b/src/slic3r/GUI/AppConfig.cpp @@ -272,7 +272,7 @@ void AppConfig::set_recent_projects(const std::vector& recent_proje } #if ENABLE_3DCONNEXION_DEVICES -void AppConfig::set_mouse_device(const std::string& name, double translation_speed, float rotation_speed) +void AppConfig::set_mouse_device(const std::string& name, double translation_speed, double translation_deadzone, float rotation_speed, float rotation_deadzone) { std::string key = std::string("mouse_device:") + name; auto it = m_storage.find(key); @@ -281,10 +281,12 @@ void AppConfig::set_mouse_device(const std::string& name, double translation_spe it->second.clear(); it->second["translation_speed"] = std::to_string(translation_speed); + it->second["translation_deadzone"] = std::to_string(translation_deadzone); it->second["rotation_speed"] = std::to_string(rotation_speed); + it->second["rotation_deadzone"] = std::to_string(rotation_deadzone); } -bool AppConfig::get_mouse_device_translation_speed(const std::string& name, double& translation_speed) +bool AppConfig::get_mouse_device_translation_speed(const std::string& name, double& speed) { std::string key = std::string("mouse_device:") + name; auto it = m_storage.find(key); @@ -295,11 +297,26 @@ bool AppConfig::get_mouse_device_translation_speed(const std::string& name, doub if (it_val == it->second.end()) return false; - translation_speed = ::atof(it_val->second.c_str()); + speed = ::atof(it_val->second.c_str()); return true; } -bool AppConfig::get_mouse_device_rotation_speed(const std::string& name, float& rotation_speed) +bool AppConfig::get_mouse_device_translation_deadzone(const std::string& name, double& deadzone) +{ + std::string key = std::string("mouse_device:") + name; + auto it = m_storage.find(key); + if (it == m_storage.end()) + return false; + + auto it_val = it->second.find("translation_deadzone"); + if (it_val == it->second.end()) + return false; + + deadzone = ::atof(it_val->second.c_str()); + return true; +} + +bool AppConfig::get_mouse_device_rotation_speed(const std::string& name, float& speed) { std::string key = std::string("mouse_device:") + name; auto it = m_storage.find(key); @@ -310,7 +327,22 @@ bool AppConfig::get_mouse_device_rotation_speed(const std::string& name, float& if (it_val == it->second.end()) return false; - rotation_speed = (float)::atof(it_val->second.c_str()); + speed = (float)::atof(it_val->second.c_str()); + return true; +} + +bool AppConfig::get_mouse_device_rotation_deadzone(const std::string& name, float& deadzone) +{ + std::string key = std::string("mouse_device:") + name; + auto it = m_storage.find(key); + if (it == m_storage.end()) + return false; + + auto it_val = it->second.find("rotation_deadzone"); + if (it_val == it->second.end()) + return false; + + deadzone = (float)::atof(it_val->second.c_str()); return true; } #endif // ENABLE_3DCONNEXION_DEVICES diff --git a/src/slic3r/GUI/AppConfig.hpp b/src/slic3r/GUI/AppConfig.hpp index 1196404a2..fce0c0b39 100644 --- a/src/slic3r/GUI/AppConfig.hpp +++ b/src/slic3r/GUI/AppConfig.hpp @@ -132,9 +132,11 @@ public: void set_recent_projects(const std::vector& recent_projects); #if ENABLE_3DCONNEXION_DEVICES - void set_mouse_device(const std::string& name, double translation_speed, float rotation_speed); - bool get_mouse_device_translation_speed(const std::string& name, double& translation_speed); - bool get_mouse_device_rotation_speed(const std::string& name, float& rotation_speed); + void set_mouse_device(const std::string& name, double translation_speed, double translation_deadzone, float rotation_speed, float rotation_deadzone); + bool get_mouse_device_translation_speed(const std::string& name, double& speed); + bool get_mouse_device_translation_deadzone(const std::string& name, double& deadzone); + bool get_mouse_device_rotation_speed(const std::string& name, float& speed); + bool get_mouse_device_rotation_deadzone(const std::string& name, float& deadzone); #endif // ENABLE_3DCONNEXION_DEVICES static const std::string SECTION_FILAMENTS; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index f80e0d0dd..5eca428b0 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2399,6 +2399,7 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) { Mouse3DController& controller = wxGetApp().plater()->get_mouse3d_controller(); controller.show_settings_dialog(!controller.is_settings_dialog_shown()); + m_dirty = true; break; } #endif // ENABLE_3DCONNEXION_DEVICES diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index c62b8c851..53bb9faa8 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -52,11 +52,15 @@ namespace Slic3r { namespace GUI { const double Mouse3DController::State::DefaultTranslationScale = 2.5; +const double Mouse3DController::State::MaxTranslationDeadzone = 0.1; +const double Mouse3DController::State::DefaultTranslationDeadzone = 0.5 * Mouse3DController::State::MaxTranslationDeadzone; const float Mouse3DController::State::DefaultRotationScale = 1.0f; +const float Mouse3DController::State::MaxRotationDeadzone = 0.1f; +const float Mouse3DController::State::DefaultRotationDeadzone = 0.5f * Mouse3DController::State::MaxRotationDeadzone; Mouse3DController::State::State() - : m_translation_scale(DefaultTranslationScale) - , m_rotation_scale(DefaultRotationScale) + : m_translation_params(DefaultTranslationScale, DefaultTranslationDeadzone) + , m_rotation_params(DefaultRotationScale, DefaultRotationDeadzone) , m_mouse_wheel_counter(0) { } @@ -87,7 +91,7 @@ bool Mouse3DController::State::apply(Camera& camera) if (has_translation()) { const Vec3d& translation = m_translation.front(); - camera.set_target(camera.get_target() + m_translation_scale * (translation(0) * camera.get_dir_right() + translation(1) * camera.get_dir_forward() + translation(2) * camera.get_dir_up())); + camera.set_target(camera.get_target() + m_translation_params.scale * (translation(0) * camera.get_dir_right() + translation(1) * camera.get_dir_forward() + translation(2) * camera.get_dir_up())); m_translation.pop(); ret = true; } @@ -95,8 +99,8 @@ bool Mouse3DController::State::apply(Camera& camera) if (has_rotation()) { const Vec3f& rotation = m_rotation.front(); - float theta = m_rotation_scale * rotation(0); - float phi = m_rotation_scale * rotation(2); + float theta = m_rotation_params.scale * rotation(0); + float phi = m_rotation_params.scale * rotation(2); float sign = camera.inverted_phi ? -1.0f : 1.0f; camera.phi += sign * phi; camera.set_theta(camera.get_theta() + theta, wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA); @@ -207,13 +211,26 @@ void Mouse3DController::render_settings_dialog(unsigned int canvas_width, unsign imgui.text(_(L("Speed:"))); ImGui::PopStyleColor(); - float translation = (float)m_state.get_translation_scale() / State::DefaultTranslationScale; - if (ImGui::SliderFloat(_(L("Translation")), &translation, 0.5f, 2.0f, "%.1f")) - m_state.set_translation_scale(State::DefaultTranslationScale * (double)translation); + float translation_scale = (float)m_state.get_translation_scale() / State::DefaultTranslationScale; + if (ImGui::SliderFloat(_(L("Translation##1")), &translation_scale, 0.5f, 2.0f, "%.1f")) + m_state.set_translation_scale(State::DefaultTranslationScale * (double)translation_scale); - float rotation = m_state.get_rotation_scale() / State::DefaultRotationScale; - if (ImGui::SliderFloat(_(L("Rotation")), &rotation, 0.5f, 2.0f, "%.1f")) - m_state.set_rotation_scale(State::DefaultRotationScale * rotation); + float rotation_scale = m_state.get_rotation_scale() / State::DefaultRotationScale; + if (ImGui::SliderFloat(_(L("Rotation##1")), &rotation_scale, 0.5f, 2.0f, "%.1f")) + m_state.set_rotation_scale(State::DefaultRotationScale * rotation_scale); + + ImGui::Separator(); + ImGui::PushStyleColor(ImGuiCol_Text, color); + imgui.text(_(L("Deadzone:"))); + ImGui::PopStyleColor(); + + float translation_deadzone = (float)m_state.get_translation_deadzone(); + if (ImGui::SliderFloat(_(L("Translation##2")), &translation_deadzone, 0.0f, (float)State::MaxTranslationDeadzone, "%.2f")) + m_state.set_translation_deadzone((double)translation_deadzone); + + float rotation_deadzone = m_state.get_rotation_deadzone(); + if (ImGui::SliderFloat(_(L("Rotation##2")), &rotation_deadzone, 0.0f, State::MaxRotationDeadzone, "%.2f")) + m_state.set_rotation_deadzone(rotation_deadzone); imgui.end(); @@ -308,13 +325,19 @@ bool Mouse3DController::connect_device() BOOST_LOG_TRIVIAL(info) << "Connected device: " << m_device_str; // get device parameters from the config, if present - double translation = 1.0; - float rotation = 1.0; - wxGetApp().app_config->get_mouse_device_translation_speed(m_device_str, translation); - wxGetApp().app_config->get_mouse_device_rotation_speed(m_device_str, rotation); + double translation_speed = 1.0; + float rotation_speed = 1.0; + double translation_deadzone = State::DefaultTranslationDeadzone; + float rotation_deadzone = State::DefaultRotationDeadzone; + wxGetApp().app_config->get_mouse_device_translation_speed(m_device_str, translation_speed); + wxGetApp().app_config->get_mouse_device_translation_deadzone(m_device_str, translation_deadzone); + wxGetApp().app_config->get_mouse_device_rotation_speed(m_device_str, rotation_speed); + wxGetApp().app_config->get_mouse_device_rotation_deadzone(m_device_str, rotation_deadzone); // clamp to valid values - m_state.set_translation_scale(State::DefaultTranslationScale * std::max(0.5, std::min(2.0, translation))); - m_state.set_rotation_scale(State::DefaultRotationScale * std::max(0.5f, std::min(2.0f, rotation))); + m_state.set_translation_scale(State::DefaultTranslationScale* std::max(0.5, std::min(2.0, translation_speed))); + m_state.set_translation_deadzone(std::max(0.0, std::min(State::MaxTranslationDeadzone, translation_deadzone))); + m_state.set_rotation_scale(State::DefaultRotationScale* std::max(0.5f, std::min(2.0f, rotation_speed))); + m_state.set_rotation_deadzone(std::max(0.0f, std::min(State::MaxRotationDeadzone, rotation_deadzone))); } return (m_device != nullptr); @@ -330,7 +353,8 @@ void Mouse3DController::disconnect_device() m_thread.join(); // Store current device parameters into the config - wxGetApp().app_config->set_mouse_device(m_device_str, m_state.get_translation_scale() / State::DefaultTranslationScale, m_state.get_rotation_scale() / State::DefaultRotationScale); + wxGetApp().app_config->set_mouse_device(m_device_str, m_state.get_translation_scale() / State::DefaultTranslationScale, m_state.get_translation_deadzone(), + m_state.get_rotation_scale() / State::DefaultRotationScale, m_state.get_rotation_deadzone()); wxGetApp().app_config->save(); // Close the 3Dconnexion device @@ -474,7 +498,9 @@ bool Mouse3DController::handle_packet_translation(const DataPacket& packet) Vec3d translation(-convert_input(packet[1], packet[2]), convert_input(packet[3], packet[4]), convert_input(packet[5], packet[6])); - if (!translation.isApprox(Vec3d::Zero())) + + double deadzone = m_state.get_translation_deadzone(); + if ((std::abs(translation(0)) > deadzone) || (std::abs(translation(1)) > deadzone) || (std::abs(translation(2)) > deadzone)) { m_state.append_translation(translation); return true; @@ -488,7 +514,9 @@ bool Mouse3DController::handle_packet_rotation(const DataPacket& packet, unsigne Vec3f rotation(-(float)convert_input(packet[first_byte + 0], packet[first_byte + 1]), (float)convert_input(packet[first_byte + 2], packet[first_byte + 3]), -(float)convert_input(packet[first_byte + 4], packet[first_byte + 5])); - if (!rotation.isApprox(Vec3f::Zero())) + + float deadzone = m_state.get_rotation_deadzone(); + if ((std::abs(rotation(0)) > deadzone) || (std::abs(rotation(1)) > deadzone) || (std::abs(rotation(2)) > deadzone)) { m_state.append_rotation(rotation); return true; diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index d7f3ed9f7..33295a68a 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -22,15 +22,28 @@ class Mouse3DController { public: static const double DefaultTranslationScale; + static const double MaxTranslationDeadzone; + static const double DefaultTranslationDeadzone; static const float DefaultRotationScale; + static const float MaxRotationDeadzone; + static const float DefaultRotationDeadzone; private: + template + struct CustomParameters + { + Number scale; + Number deadzone; + + CustomParameters(Number scale, Number deadzone) : scale(scale), deadzone(deadzone) {} + }; + std::queue m_translation; std::queue m_rotation; std::queue m_buttons; - double m_translation_scale; - float m_rotation_scale; + CustomParameters m_translation_params; + CustomParameters m_rotation_params; // When the 3Dconnexion driver is running the system gets, by default, mouse wheel events when rotations around the X axis are detected. // We want to filter these out because we are getting the data directly from the device, bypassing the driver, and those mouse wheel events interfere @@ -59,11 +72,17 @@ class Mouse3DController bool process_mouse_wheel(); - double get_translation_scale() const { return m_translation_scale; } - void set_translation_scale(double scale) { m_translation_scale = scale; } + double get_translation_scale() const { return m_translation_params.scale; } + void set_translation_scale(double scale) { m_translation_params.scale = scale; } - float get_rotation_scale() const { return m_rotation_scale; } - void set_rotation_scale(float scale) { m_rotation_scale = scale; } + float get_rotation_scale() const { return m_rotation_params.scale; } + void set_rotation_scale(float scale) { m_rotation_params.scale = scale; } + + double get_translation_deadzone() const { return m_translation_params.deadzone; } + void set_translation_deadzone(double deadzone) { m_translation_params.deadzone = deadzone; } + + float get_rotation_deadzone() const { return m_rotation_params.deadzone; } + void set_rotation_deadzone(float deadzone) { m_rotation_params.deadzone = deadzone; } // return true if any change to the camera took place bool apply(Camera& camera); From b41a0656b99860b3b6e1f410bb0ca57fe2c0fa59 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 9 Oct 2019 14:01:13 +0200 Subject: [PATCH 18/67] ENABLE_3DCONNEXION_DEVICES -> Reworked parsing of data coming from device --- src/slic3r/GUI/Mouse3DController.cpp | 57 ++++++++-------------------- 1 file changed, 15 insertions(+), 42 deletions(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 53bb9faa8..86f6fa5a7 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -14,6 +14,8 @@ #include #include "I18N.hpp" +#include + // WARN: If updating these lists, please also update resources/udev/90-3dconnexion.rules static const std::vector _3DCONNEXION_VENDORS = @@ -36,16 +38,16 @@ static const std::vector _3DCONNEXION_DEVICES = 0xc628, /* 50728 space navigator for notebooks*/ 0xc629, /* 50729 space pilot pro*/ 0xc62b, /* 50731 space mouse pro*/ - 0xc62e, /* 50734 spacemouse wireless (USB cable) */ - 0xc62f, /* 50735 spacemouse wireless receiver */ + 0xc62e, /* 50734 spacemouse wireless (USB cable) *TESTED* */ + 0xc62f, /* 50735 spacemouse wireless receiver */ 0xc631, /* 50737 spacemouse pro wireless *TESTED* */ 0xc632, /* 50738 spacemouse pro wireless receiver */ 0xc633, /* 50739 spacemouse enterprise */ - 0xc635, /* 50741 spacemouse compact */ + 0xc635, /* 50741 spacemouse compact *TESTED* */ 0xc636, /* 50742 spacemouse module */ 0xc640, /* 50752 nulooq */ -// 0xc652, /* 50770 3Dconnexion universal receiver */ +// 0xc652, /* 50770 3Dconnexion universal receiver */ }; namespace Slic3r { @@ -254,26 +256,6 @@ bool Mouse3DController::connect_device() unsigned short vendor_id = 0; unsigned short product_id = 0; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - hid_device_info* cur = devices; - while (cur != nullptr) - { - std::cout << "Detected device '"; - - if (cur->manufacturer_string != nullptr) - std::wcout << cur->manufacturer_string << L" "; - else - std::wcout << "MMM "; - if (cur->product_string != nullptr) - std::wcout << cur->product_string; - else - std::wcout << "PPP"; - std::cout << "' code: " << cur->product_id << " (" << std::hex << cur->product_id << std::dec << ")" << std::endl; - - cur = cur->next; - } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - hid_device_info* current = devices; while (current != nullptr) { @@ -481,17 +463,9 @@ bool Mouse3DController::handle_wireless_packet(const DataPacket& packet) double convert_input(unsigned char first, unsigned char second) { - int ret = 0; - switch (second) - { - case 0: { ret = (int)first; break; } - case 1: { ret = (int)first + 255; break; } - case 254: { ret = -511 + (int)first; break; } - case 255: { ret = -255 + (int)first; break; } - default: { break; } - } - return (double)ret / 349.0; -}; + short value = first | second << 8; + return (double)value / 350.0; +} bool Mouse3DController::handle_packet_translation(const DataPacket& packet) { @@ -527,15 +501,14 @@ bool Mouse3DController::handle_packet_rotation(const DataPacket& packet, unsigne bool Mouse3DController::handle_packet_button(const DataPacket& packet, unsigned int packet_size) { - for (unsigned int i = 0, base = 0; i < packet_size; ++i, base += 8) + unsigned int data = packet[1] | packet[2] << 8 | packet[3] << 16 | packet[4] << 24; + const std::bitset<32> data_bits{ data }; + for (size_t i = 0; i < data_bits.size(); ++i) { - for (unsigned int j = 0; j < 8; ++j) + if (data_bits.test(i)) { - if (packet[i + 1] & (0x1 << j)) - { - m_state.append_button(base + j); - return true; - } + m_state.append_button((unsigned int)i); + return true; } } From a735ec1b48c159403637dcd202a4ca4d98278821 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 9 Oct 2019 14:18:43 +0200 Subject: [PATCH 19/67] ENABLE_3DCONNEXION_DEVICES -> Added tech ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT for debug output --- src/libslic3r/Technologies.hpp | 2 ++ src/slic3r/GUI/Mouse3DController.cpp | 28 +++++++++++++++++++++++++++- src/slic3r/GUI/Mouse3DController.hpp | 8 +++++++- 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index da982c49a..f64bea172 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -39,6 +39,8 @@ // Enabled 3Dconnexion devices #define ENABLE_3DCONNEXION_DEVICES (1 && ENABLE_2_0_0_ALPHA1) +// Enabled 3Dconnexion devices debug output +#define ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT (1 && ENABLE_3DCONNEXION_DEVICES) #endif // _technologies_h_ diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 86f6fa5a7..00d5e184a 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -111,7 +111,7 @@ bool Mouse3DController::State::apply(Camera& camera) ret = true; } - if (has_any_button()) + if (has_button()) { unsigned int button = m_buttons.front(); switch (button) @@ -234,6 +234,18 @@ void Mouse3DController::render_settings_dialog(unsigned int canvas_width, unsign if (ImGui::SliderFloat(_(L("Rotation##2")), &rotation_deadzone, 0.0f, State::MaxRotationDeadzone, "%.2f")) m_state.set_rotation_deadzone(rotation_deadzone); +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + ImGui::Separator(); + ImGui::PushStyleColor(ImGuiCol_Text, color); + imgui.text(_(L("DEBUG:"))); + ImGui::PopStyleColor(); + Vec3f translation = m_state.get_translation().cast(); + Vec3f rotation = m_state.get_rotation(); + unsigned int button = m_state.get_button(); + ImGui::InputFloat3("Translation##3", translation.data(), "%.3f", ImGuiInputTextFlags_ReadOnly); + ImGui::InputFloat3("Rotation##3", rotation.data(), "%.3f", ImGuiInputTextFlags_ReadOnly); +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + imgui.end(); ImGui::PopStyleVar(); @@ -256,6 +268,20 @@ bool Mouse3DController::connect_device() unsigned short vendor_id = 0; unsigned short product_id = 0; +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + hid_device_info* cur = devices; + while (cur != nullptr) + { + std::cout << "Detected device '"; + std::wcout << ((cur->manufacturer_string != nullptr) ? cur->manufacturer_string : L"Unknown"); + std::cout << "::"; + std::wcout << ((cur->product_string != nullptr) ? cur->product_string : L"Unknown"); + std::cout << "' code: " << cur->vendor_id << "/" << cur->product_id << " (" << std::hex << cur->vendor_id << "/" << cur->product_id << std::dec << ")" << std::endl; + + cur = cur->next; + } +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + hid_device_info* current = devices; while (current != nullptr) { diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index 33295a68a..3136f9396 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -68,7 +68,13 @@ class Mouse3DController bool has_translation() const { return !m_translation.empty(); } bool has_rotation() const { return !m_rotation.empty(); } - bool has_any_button() const { return !m_buttons.empty(); } + bool has_button() const { return !m_buttons.empty(); } + +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + Vec3d get_translation() const { return has_translation() ? m_translation.front() : Vec3d::Zero(); } + Vec3f get_rotation() const { return has_rotation() ? m_rotation.front() : Vec3f::Zero(); } + unsigned int get_button() const { return has_button() ? m_buttons.front() : 0; } +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT bool process_mouse_wheel(); From 58884774737cd1dff406d4226d618ec2acf7fb40 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 9 Oct 2019 14:39:28 +0200 Subject: [PATCH 20/67] ENABLE_3DCONNEXION_DEVICES -> Reworked deadzone check --- src/slic3r/GUI/Mouse3DController.cpp | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 00d5e184a..0462e21e2 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -487,20 +487,21 @@ bool Mouse3DController::handle_wireless_packet(const DataPacket& packet) return false; } -double convert_input(unsigned char first, unsigned char second) +double convert_input(unsigned char first, unsigned char second, double deadzone) { short value = first | second << 8; - return (double)value / 350.0; + double ret = (double)value / 350.0; + return (std::abs(ret) > deadzone) ? ret : 0.0; } bool Mouse3DController::handle_packet_translation(const DataPacket& packet) { - Vec3d translation(-convert_input(packet[1], packet[2]), - convert_input(packet[3], packet[4]), - convert_input(packet[5], packet[6])); - double deadzone = m_state.get_translation_deadzone(); - if ((std::abs(translation(0)) > deadzone) || (std::abs(translation(1)) > deadzone) || (std::abs(translation(2)) > deadzone)) + Vec3d translation(-convert_input(packet[1], packet[2], deadzone), + convert_input(packet[3], packet[4], deadzone), + convert_input(packet[5], packet[6], deadzone)); + + if (!translation.isApprox(Vec3d::Zero())) { m_state.append_translation(translation); return true; @@ -511,12 +512,12 @@ bool Mouse3DController::handle_packet_translation(const DataPacket& packet) bool Mouse3DController::handle_packet_rotation(const DataPacket& packet, unsigned int first_byte) { - Vec3f rotation(-(float)convert_input(packet[first_byte + 0], packet[first_byte + 1]), - (float)convert_input(packet[first_byte + 2], packet[first_byte + 3]), - -(float)convert_input(packet[first_byte + 4], packet[first_byte + 5])); + double deadzone = (double)m_state.get_rotation_deadzone(); + Vec3f rotation(-(float)convert_input(packet[first_byte + 0], packet[first_byte + 1], deadzone), + (float)convert_input(packet[first_byte + 2], packet[first_byte + 3], deadzone), + -(float)convert_input(packet[first_byte + 4], packet[first_byte + 5], deadzone)); - float deadzone = m_state.get_rotation_deadzone(); - if ((std::abs(rotation(0)) > deadzone) || (std::abs(rotation(1)) > deadzone) || (std::abs(rotation(2)) > deadzone)) + if (!rotation.isApprox(Vec3f::Zero())) { m_state.append_rotation(rotation); return true; From 3a5823bc2eda0ef7ce1619e4fe852ee2b926247d Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 9 Oct 2019 15:23:30 +0200 Subject: [PATCH 21/67] ENABLE_3DCONNEXION_DEVICES -> Added temporary debug output to measure input queue sizes --- src/slic3r/GUI/Mouse3DController.cpp | 45 +++++++++++++++++++++++- src/slic3r/GUI/Mouse3DController.hpp | 52 ++++++++++++++++++++++++++-- 2 files changed, 94 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 0462e21e2..e22476671 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -64,6 +64,13 @@ Mouse3DController::State::State() : m_translation_params(DefaultTranslationScale, DefaultTranslationDeadzone) , m_rotation_params(DefaultRotationScale, DefaultRotationDeadzone) , m_mouse_wheel_counter(0) +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + , m_translation_queue_max_size(0) + , m_rotation_queue_max_size(0) + , m_buttons_queue_max_size(0) +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ { } @@ -235,15 +242,51 @@ void Mouse3DController::render_settings_dialog(unsigned int canvas_width, unsign m_state.set_rotation_deadzone(rotation_deadzone); #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + ImGui::Separator(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ImGui::Separator(); ImGui::PushStyleColor(ImGuiCol_Text, color); - imgui.text(_(L("DEBUG:"))); + imgui.text("DEBUG:"); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + imgui.text("Vectors:"); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ImGui::PopStyleColor(); Vec3f translation = m_state.get_translation().cast(); Vec3f rotation = m_state.get_rotation(); unsigned int button = m_state.get_button(); ImGui::InputFloat3("Translation##3", translation.data(), "%.3f", ImGuiInputTextFlags_ReadOnly); ImGui::InputFloat3("Rotation##3", rotation.data(), "%.3f", ImGuiInputTextFlags_ReadOnly); + +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + ImGui::PushStyleColor(ImGuiCol_Text, color); + imgui.text("Queue size:"); + ImGui::PopStyleColor(); + + int translation_size[2] = { (int)m_state.get_translation_queue_size(), (int)m_state.get_translation_queue_max_size() }; + int rotation_size[2] = { (int)m_state.get_rotation_queue_size(), (int)m_state.get_rotation_queue_max_size() }; + int buttons_size[2] = { (int)m_state.get_buttons_queue_size(), (int)m_state.get_buttons_queue_max_size() }; + + ImGui::InputInt2("Translation##4", translation_size, ImGuiInputTextFlags_ReadOnly); + ImGui::InputInt2("Rotation##4", rotation_size, ImGuiInputTextFlags_ReadOnly); + ImGui::InputInt2("Buttons", buttons_size, ImGuiInputTextFlags_ReadOnly); + +/* + int translation_size = (int)m_state.get_translation_queue_size(); + int translation_max_size = (int)m_state.get_translation_queue_max_size(); + int rotation_size = (int)m_state.get_rotation_queue_size(); + int rotation_max_size = (int)m_state.get_rotation_queue_max_size(); + int buttons_size = (int)m_state.get_buttons_queue_size(); + int buttons_max_size = (int)m_state.get_buttons_queue_max_size(); + + ImGui::InputInt("Translation size", &translation_size, 0, 0, ImGuiInputTextFlags_ReadOnly); + ImGui::InputInt("Translation max", &translation_max_size, 0, 0, ImGuiInputTextFlags_ReadOnly); + ImGui::InputInt("Rotation size", &rotation_size, 0, 0, ImGuiInputTextFlags_ReadOnly); + ImGui::InputInt("Rotation max", &rotation_max_size, 0, 0, ImGuiInputTextFlags_ReadOnly); + ImGui::InputInt("Button size", &buttons_size, 0, 0, ImGuiInputTextFlags_ReadOnly); + ImGui::InputInt("Button max", &buttons_max_size, 0, 0, ImGuiInputTextFlags_ReadOnly); +*/ +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT imgui.end(); diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index 3136f9396..2f3514048 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -54,17 +54,53 @@ class Mouse3DController // GLCanvas3D::on_idle() through the call to the apply() method unsigned int m_mouse_wheel_counter; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + unsigned int m_translation_queue_max_size; + unsigned int m_rotation_queue_max_size; + unsigned int m_buttons_queue_max_size; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + public: State(); - void append_translation(const Vec3d& translation) { m_translation.push(translation); } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + void append_translation(const Vec3d& translation) + { + m_translation.push(translation); +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + m_translation_queue_max_size = std::max(m_translation_queue_max_size, (unsigned int)m_translation.size()); +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + } + void append_rotation(const Vec3f& rotation) { m_rotation.push(rotation); +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + m_rotation_queue_max_size = std::max(m_rotation_queue_max_size, (unsigned int)m_rotation.size()); +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT if (rotation(0) != 0.0f) ++m_mouse_wheel_counter; } - void append_button(unsigned int id) { m_buttons.push(id); } + + void append_button(unsigned int id) + { + m_buttons.push(id); +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + m_buttons_queue_max_size = std::max(m_buttons_queue_max_size, (unsigned int)m_buttons.size()); +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + } + +// void append_translation(const Vec3d& translation) { m_translation.push(translation); } +// void append_rotation(const Vec3f& rotation) +// { +// m_rotation.push(rotation); +// if (rotation(0) != 0.0f) +// ++m_mouse_wheel_counter; +// } +// void append_button(unsigned int id) { m_buttons.push(id); } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ bool has_translation() const { return !m_translation.empty(); } bool has_rotation() const { return !m_rotation.empty(); } @@ -90,6 +126,18 @@ class Mouse3DController float get_rotation_deadzone() const { return m_rotation_params.deadzone; } void set_rotation_deadzone(float deadzone) { m_rotation_params.deadzone = deadzone; } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + unsigned int get_translation_queue_size() const { return (unsigned int)m_translation.size(); } + unsigned int get_rotation_queue_size() const { return (unsigned int)m_rotation.size(); } + unsigned int get_buttons_queue_size() const { return (unsigned int)m_buttons.size(); } + + unsigned int get_translation_queue_max_size() const { return m_translation_queue_max_size; } + unsigned int get_rotation_queue_max_size() const { return m_rotation_queue_max_size; } + unsigned int get_buttons_queue_max_size() const { return m_buttons_queue_max_size; } +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + // return true if any change to the camera took place bool apply(Camera& camera); }; From aae1250921491814dffe4fd340ca293d6d23284b Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 10 Oct 2019 09:04:44 +0200 Subject: [PATCH 22/67] ENABLE_3DCONNEXION_DEVICES -> Experimental input queues with max size --- src/slic3r/GUI/Mouse3DController.cpp | 106 ++++++++++++++++++++----- src/slic3r/GUI/Mouse3DController.hpp | 111 ++++++++++++++++++++++++--- 2 files changed, 190 insertions(+), 27 deletions(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index e22476671..10f365dd7 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -54,10 +54,10 @@ namespace Slic3r { namespace GUI { const double Mouse3DController::State::DefaultTranslationScale = 2.5; -const double Mouse3DController::State::MaxTranslationDeadzone = 0.1; +const double Mouse3DController::State::MaxTranslationDeadzone = 0.2; const double Mouse3DController::State::DefaultTranslationDeadzone = 0.5 * Mouse3DController::State::MaxTranslationDeadzone; const float Mouse3DController::State::DefaultRotationScale = 1.0f; -const float Mouse3DController::State::MaxRotationDeadzone = 0.1f; +const float Mouse3DController::State::MaxRotationDeadzone = (float)Mouse3DController::State::MaxTranslationDeadzone; const float Mouse3DController::State::DefaultRotationDeadzone = 0.5f * Mouse3DController::State::MaxRotationDeadzone; Mouse3DController::State::State() @@ -78,7 +78,15 @@ bool Mouse3DController::State::process_mouse_wheel() { if (m_mouse_wheel_counter == 0) return false; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_QUEUE_MAX_SIZE + else if (!m_rotation.queue.empty()) +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ else if (!m_rotation.empty()) +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_QUEUE_MAX_SIZE +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ { --m_mouse_wheel_counter; return true; @@ -99,14 +107,35 @@ bool Mouse3DController::State::apply(Camera& camera) if (has_translation()) { +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_QUEUE_MAX_SIZE + const Vec3d& translation = m_translation.queue.front(); + camera.set_target(camera.get_target() + m_translation_params.scale * (translation(0) * camera.get_dir_right() + translation(1) * camera.get_dir_forward() + translation(2) * camera.get_dir_up())); + m_translation.queue.pop(); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ const Vec3d& translation = m_translation.front(); camera.set_target(camera.get_target() + m_translation_params.scale * (translation(0) * camera.get_dir_right() + translation(1) * camera.get_dir_forward() + translation(2) * camera.get_dir_up())); m_translation.pop(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_QUEUE_MAX_SIZE +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ret = true; } if (has_rotation()) { +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_QUEUE_MAX_SIZE + const Vec3f& rotation = m_rotation.queue.front(); + float theta = m_rotation_params.scale * rotation(0); + float phi = m_rotation_params.scale * rotation(2); + float sign = camera.inverted_phi ? -1.0f : 1.0f; + camera.phi += sign * phi; + camera.set_theta(camera.get_theta() + theta, wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA); + m_rotation.queue.pop(); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ const Vec3f& rotation = m_rotation.front(); float theta = m_rotation_params.scale * rotation(0); float phi = m_rotation_params.scale * rotation(2); @@ -114,12 +143,27 @@ bool Mouse3DController::State::apply(Camera& camera) camera.phi += sign * phi; camera.set_theta(camera.get_theta() + theta, wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA); m_rotation.pop(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_QUEUE_MAX_SIZE +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ret = true; } if (has_button()) { +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_QUEUE_MAX_SIZE + unsigned int button = m_buttons.queue.front(); + switch (button) + { + case 0: { camera.update_zoom(1.0); break; } + case 1: { camera.update_zoom(-1.0); break; } + default: { break; } + } + m_buttons.queue.pop(); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ unsigned int button = m_buttons.front(); switch (button) { @@ -128,6 +172,9 @@ bool Mouse3DController::State::apply(Camera& camera) default: { break; } } m_buttons.pop(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_QUEUE_MAX_SIZE +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ret = true; } @@ -271,21 +318,14 @@ void Mouse3DController::render_settings_dialog(unsigned int canvas_width, unsign ImGui::InputInt2("Rotation##4", rotation_size, ImGuiInputTextFlags_ReadOnly); ImGui::InputInt2("Buttons", buttons_size, ImGuiInputTextFlags_ReadOnly); -/* - int translation_size = (int)m_state.get_translation_queue_size(); - int translation_max_size = (int)m_state.get_translation_queue_max_size(); - int rotation_size = (int)m_state.get_rotation_queue_size(); - int rotation_max_size = (int)m_state.get_rotation_queue_max_size(); - int buttons_size = (int)m_state.get_buttons_queue_size(); - int buttons_max_size = (int)m_state.get_buttons_queue_max_size(); - - ImGui::InputInt("Translation size", &translation_size, 0, 0, ImGuiInputTextFlags_ReadOnly); - ImGui::InputInt("Translation max", &translation_max_size, 0, 0, ImGuiInputTextFlags_ReadOnly); - ImGui::InputInt("Rotation size", &rotation_size, 0, 0, ImGuiInputTextFlags_ReadOnly); - ImGui::InputInt("Rotation max", &rotation_max_size, 0, 0, ImGuiInputTextFlags_ReadOnly); - ImGui::InputInt("Button size", &buttons_size, 0, 0, ImGuiInputTextFlags_ReadOnly); - ImGui::InputInt("Button max", &buttons_max_size, 0, 0, ImGuiInputTextFlags_ReadOnly); -*/ +#if ENABLE_QUEUE_MAX_SIZE + int queue_size = (int)m_state.get_queues_max_size(); + if (ImGui::InputInt("Max size", &queue_size, 1, 1, ImGuiInputTextFlags_ReadOnly)) + { + if (queue_size > 0) + m_state.set_queues_max_size(queue_size); + } +#endif // ENABLE_QUEUE_MAX_SIZE //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT @@ -312,6 +352,10 @@ bool Mouse3DController::connect_device() unsigned short product_id = 0; #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + unsigned short usage_page = 0; + unsigned short usage = 0; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ hid_device_info* cur = devices; while (cur != nullptr) { @@ -344,6 +388,12 @@ bool Mouse3DController::connect_device() if (_3DCONNEXION_DEVICES[i] == current->product_id) { product_id = current->product_id; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + usage_page = current->usage_page; + usage = current->usage; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ break; } } @@ -369,11 +419,31 @@ bool Mouse3DController::connect_device() if (m_device != nullptr) { +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + std::vector manufacturer(1024, 0); + hid_get_manufacturer_string(m_device, manufacturer.data(), 1024); + m_device_str = boost::nowide::narrow(manufacturer.data()); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + std::vector product(1024, 0); hid_get_product_string(m_device, product.data(), 1024); - m_device_str = boost::nowide::narrow(product.data()); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + m_device_str += "/" + boost::nowide::narrow(product.data()); +// m_device_str = boost::nowide::narrow(product.data()); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ BOOST_LOG_TRIVIAL(info) << "Connected device: " << m_device_str; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + std::cout << std::endl << "Connected device:" << std::endl; + std::cout << "Manufacturer/product..........: " << m_device_str << std::endl; + std::cout << "Manufacturer id/product id....: " << vendor_id << "/" << product_id << std::endl; + std::cout << "Manufacturer id/product id hex: " << std::hex << vendor_id << "/" << product_id << std::dec << std::endl; + std::cout << "Usage page....................: " << usage_page << std::endl; + std::cout << "Usage.........................: " << usage << std::endl; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + // get device parameters from the config, if present double translation_speed = 1.0; diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index 2f3514048..c06f0863e 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -11,6 +11,10 @@ #include #include +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#define ENABLE_QUEUE_MAX_SIZE 1 +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + namespace Slic3r { namespace GUI { @@ -38,9 +42,27 @@ class Mouse3DController CustomParameters(Number scale, Number deadzone) : scale(scale), deadzone(deadzone) {} }; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_QUEUE_MAX_SIZE + template + struct InputQueue + { + size_t max_size{ 20 }; + std::queue queue; + }; + + InputQueue m_translation; + InputQueue m_rotation; + InputQueue m_buttons; +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ std::queue m_translation; std::queue m_rotation; std::queue m_buttons; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_QUEUE_MAX_SIZE +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + CustomParameters m_translation_params; CustomParameters m_rotation_params; @@ -68,48 +90,92 @@ class Mouse3DController //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void append_translation(const Vec3d& translation) { +#if ENABLE_QUEUE_MAX_SIZE + while (m_translation.queue.size() >= m_translation.max_size) + { + m_translation.queue.pop(); + } + m_translation.queue.push(translation); +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + m_translation_queue_max_size = std::max(m_translation_queue_max_size, (unsigned int)m_translation.queue.size()); +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +#else m_translation.push(translation); #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT m_translation_queue_max_size = std::max(m_translation_queue_max_size, (unsigned int)m_translation.size()); #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +#endif // ENABLE_QUEUE_MAX_SIZE } void append_rotation(const Vec3f& rotation) { +#if ENABLE_QUEUE_MAX_SIZE + while (m_rotation.queue.size() >= m_rotation.max_size) + { + m_rotation.queue.pop(); + } + m_rotation.queue.push(rotation); +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + m_rotation_queue_max_size = std::max(m_rotation_queue_max_size, (unsigned int)m_rotation.queue.size()); +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + if (rotation(0) != 0.0f) + ++m_mouse_wheel_counter; +#else m_rotation.push(rotation); #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT m_rotation_queue_max_size = std::max(m_rotation_queue_max_size, (unsigned int)m_rotation.size()); #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT if (rotation(0) != 0.0f) ++m_mouse_wheel_counter; +#endif // ENABLE_QUEUE_MAX_SIZE } void append_button(unsigned int id) { +#if ENABLE_QUEUE_MAX_SIZE + while (m_buttons.queue.size() >= m_buttons.max_size) + { + m_buttons.queue.pop(); + } + m_buttons.queue.push(id); +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + m_buttons_queue_max_size = std::max(m_buttons_queue_max_size, (unsigned int)m_buttons.queue.size()); +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +#else m_buttons.push(id); #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT m_buttons_queue_max_size = std::max(m_buttons_queue_max_size, (unsigned int)m_buttons.size()); #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +#endif // ENABLE_QUEUE_MAX_SIZE } - -// void append_translation(const Vec3d& translation) { m_translation.push(translation); } -// void append_rotation(const Vec3f& rotation) -// { -// m_rotation.push(rotation); -// if (rotation(0) != 0.0f) -// ++m_mouse_wheel_counter; -// } -// void append_button(unsigned int id) { m_buttons.push(id); } //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_QUEUE_MAX_SIZE + bool has_translation() const { return !m_translation.queue.empty(); } + bool has_rotation() const { return !m_rotation.queue.empty(); } + bool has_button() const { return !m_buttons.queue.empty(); } +#else bool has_translation() const { return !m_translation.empty(); } bool has_rotation() const { return !m_rotation.empty(); } bool has_button() const { return !m_buttons.empty(); } +#endif // ENABLE_QUEUE_MAX_SIZE +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_QUEUE_MAX_SIZE + Vec3d get_translation() const { return has_translation() ? m_translation.queue.front() : Vec3d::Zero(); } + Vec3f get_rotation() const { return has_rotation() ? m_rotation.queue.front() : Vec3f::Zero(); } + unsigned int get_button() const { return has_button() ? m_buttons.queue.front() : 0; } +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Vec3d get_translation() const { return has_translation() ? m_translation.front() : Vec3d::Zero(); } Vec3f get_rotation() const { return has_rotation() ? m_rotation.front() : Vec3f::Zero(); } unsigned int get_button() const { return has_button() ? m_buttons.front() : 0; } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_QUEUE_MAX_SIZE +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT bool process_mouse_wheel(); @@ -128,14 +194,41 @@ class Mouse3DController //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +#if ENABLE_QUEUE_MAX_SIZE + unsigned int get_translation_queue_size() const { return (unsigned int)m_translation.queue.size(); } + unsigned int get_rotation_queue_size() const { return (unsigned int)m_rotation.queue.size(); } + unsigned int get_buttons_queue_size() const { return (unsigned int)m_buttons.queue.size(); } +#else unsigned int get_translation_queue_size() const { return (unsigned int)m_translation.size(); } unsigned int get_rotation_queue_size() const { return (unsigned int)m_rotation.size(); } unsigned int get_buttons_queue_size() const { return (unsigned int)m_buttons.size(); } +#endif // ENABLE_QUEUE_MAX_SIZE unsigned int get_translation_queue_max_size() const { return m_translation_queue_max_size; } unsigned int get_rotation_queue_max_size() const { return m_rotation_queue_max_size; } unsigned int get_buttons_queue_max_size() const { return m_buttons_queue_max_size; } #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_QUEUE_MAX_SIZE + size_t get_queues_max_size() const { return m_translation.max_size; } + void set_queues_max_size(size_t size) + { + if (size > 0) + { + std::cout << "New queues max size: " << size << std::endl; + + m_translation.max_size = size; + m_rotation.max_size = size; + m_buttons.max_size = size; + + m_translation_queue_max_size = 0; + m_rotation_queue_max_size = 0; + m_buttons_queue_max_size = 0; + } + } +#endif // ENABLE_QUEUE_MAX_SIZE //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // return true if any change to the camera took place From ded6d443899945c14c5206e65c0858950c00fd81 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 10 Oct 2019 09:29:55 +0200 Subject: [PATCH 23/67] Fixed build on OsX --- src/slic3r/GUI/Mouse3DController.cpp | 1 - src/slic3r/GUI/Mouse3DController.hpp | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 10f365dd7..f0b78a778 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -301,7 +301,6 @@ void Mouse3DController::render_settings_dialog(unsigned int canvas_width, unsign ImGui::PopStyleColor(); Vec3f translation = m_state.get_translation().cast(); Vec3f rotation = m_state.get_rotation(); - unsigned int button = m_state.get_button(); ImGui::InputFloat3("Translation##3", translation.data(), "%.3f", ImGuiInputTextFlags_ReadOnly); ImGui::InputFloat3("Rotation##3", rotation.data(), "%.3f", ImGuiInputTextFlags_ReadOnly); diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index c06f0863e..a3eab740f 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -47,8 +47,10 @@ class Mouse3DController template struct InputQueue { - size_t max_size{ 20 }; + size_t max_size; std::queue queue; + + InputQueue() : max_size(20) {} }; InputQueue m_translation; From f4654e376eabed8f9a72de1a4d1107302342e1fc Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 10 Oct 2019 10:49:47 +0200 Subject: [PATCH 24/67] ENABLE_3DCONNEXION_DEVICES -> Slightly increased camera target movement limits --- src/slic3r/GUI/Camera.cpp | 15 ++++++++++++--- src/slic3r/GUI/Mouse3DController.cpp | 21 ++------------------- src/slic3r/GUI/Mouse3DController.hpp | 28 ++++++++++++++++++++-------- 3 files changed, 34 insertions(+), 30 deletions(-) diff --git a/src/slic3r/GUI/Camera.cpp b/src/slic3r/GUI/Camera.cpp index 4bb3e6ad5..e43b57636 100644 --- a/src/slic3r/GUI/Camera.cpp +++ b/src/slic3r/GUI/Camera.cpp @@ -85,10 +85,21 @@ void Camera::select_next_type() void Camera::set_target(const Vec3d& target) { +#if ENABLE_3DCONNEXION_DEVICES + // We may let these factors be customizable + static const double ScaleFactor = 1.1; + BoundingBoxf3 test_box = m_scene_box; + test_box.scale(ScaleFactor); + m_target = target; + m_target(0) = clamp(test_box.min(0), test_box.max(0), m_target(0)); + m_target(1) = clamp(test_box.min(1), test_box.max(1), m_target(1)); + m_target(2) = clamp(test_box.min(2), test_box.max(2), m_target(2)); +#else m_target = target; m_target(0) = clamp(m_scene_box.min(0), m_scene_box.max(0), m_target(0)); m_target(1) = clamp(m_scene_box.min(1), m_scene_box.max(1), m_target(1)); m_target(2) = clamp(m_scene_box.min(2), m_scene_box.max(2), m_target(2)); +#endif // ENABLE_3DCONNEXION_DEVICES } void Camera::set_theta(float theta, bool apply_limit) @@ -117,9 +128,7 @@ void Camera::set_zoom(double zoom) zoom = std::max(zoom, zoom_min * 0.7); // Don't allow to zoom too close to the scene. - zoom = std::min(zoom, 100.0); - - m_zoom = zoom; + m_zoom = std::min(zoom, 100.0); } #else void Camera::set_zoom(double zoom, const BoundingBoxf3& max_box, int canvas_w, int canvas_h) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index f0b78a778..8cf7be33e 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -64,13 +64,11 @@ Mouse3DController::State::State() : m_translation_params(DefaultTranslationScale, DefaultTranslationDeadzone) , m_rotation_params(DefaultRotationScale, DefaultRotationDeadzone) , m_mouse_wheel_counter(0) -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT , m_translation_queue_max_size(0) , m_rotation_queue_max_size(0) , m_buttons_queue_max_size(0) #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ { } @@ -289,22 +287,17 @@ void Mouse3DController::render_settings_dialog(unsigned int canvas_width, unsign m_state.set_rotation_deadzone(rotation_deadzone); #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ImGui::Separator(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ImGui::Separator(); ImGui::PushStyleColor(ImGuiCol_Text, color); imgui.text("DEBUG:"); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ imgui.text("Vectors:"); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ImGui::PopStyleColor(); Vec3f translation = m_state.get_translation().cast(); Vec3f rotation = m_state.get_rotation(); ImGui::InputFloat3("Translation##3", translation.data(), "%.3f", ImGuiInputTextFlags_ReadOnly); ImGui::InputFloat3("Rotation##3", rotation.data(), "%.3f", ImGuiInputTextFlags_ReadOnly); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ImGui::PushStyleColor(ImGuiCol_Text, color); imgui.text("Queue size:"); ImGui::PopStyleColor(); @@ -317,6 +310,7 @@ void Mouse3DController::render_settings_dialog(unsigned int canvas_width, unsign ImGui::InputInt2("Rotation##4", rotation_size, ImGuiInputTextFlags_ReadOnly); ImGui::InputInt2("Buttons", buttons_size, ImGuiInputTextFlags_ReadOnly); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_QUEUE_MAX_SIZE int queue_size = (int)m_state.get_queues_max_size(); if (ImGui::InputInt("Max size", &queue_size, 1, 1, ImGuiInputTextFlags_ReadOnly)) @@ -351,10 +345,8 @@ bool Mouse3DController::connect_device() unsigned short product_id = 0; #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ unsigned short usage_page = 0; unsigned short usage = 0; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ hid_device_info* cur = devices; while (cur != nullptr) { @@ -387,12 +379,10 @@ bool Mouse3DController::connect_device() if (_3DCONNEXION_DEVICES[i] == current->product_id) { product_id = current->product_id; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT usage_page = current->usage_page; usage = current->usage; #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ break; } } @@ -418,21 +408,16 @@ bool Mouse3DController::connect_device() if (m_device != nullptr) { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ std::vector manufacturer(1024, 0); hid_get_manufacturer_string(m_device, manufacturer.data(), 1024); m_device_str = boost::nowide::narrow(manufacturer.data()); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ std::vector product(1024, 0); hid_get_product_string(m_device, product.data(), 1024); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_device_str += "/" + boost::nowide::narrow(product.data()); -// m_device_str = boost::nowide::narrow(product.data()); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ BOOST_LOG_TRIVIAL(info) << "Connected device: " << m_device_str; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT std::cout << std::endl << "Connected device:" << std::endl; std::cout << "Manufacturer/product..........: " << m_device_str << std::endl; @@ -441,8 +426,6 @@ bool Mouse3DController::connect_device() std::cout << "Usage page....................: " << usage_page << std::endl; std::cout << "Usage.........................: " << usage << std::endl; #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - // get device parameters from the config, if present double translation_speed = 1.0; diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index a3eab740f..f31bdc913 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -78,20 +78,18 @@ class Mouse3DController // GLCanvas3D::on_idle() through the call to the apply() method unsigned int m_mouse_wheel_counter; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT unsigned int m_translation_queue_max_size; unsigned int m_rotation_queue_max_size; unsigned int m_buttons_queue_max_size; #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ public: State(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void append_translation(const Vec3d& translation) { +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_QUEUE_MAX_SIZE while (m_translation.queue.size() >= m_translation.max_size) { @@ -102,15 +100,19 @@ class Mouse3DController m_translation_queue_max_size = std::max(m_translation_queue_max_size, (unsigned int)m_translation.queue.size()); #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT #else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_translation.push(translation); #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT m_translation_queue_max_size = std::max(m_translation_queue_max_size, (unsigned int)m_translation.size()); #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_QUEUE_MAX_SIZE +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } void append_rotation(const Vec3f& rotation) { +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_QUEUE_MAX_SIZE while (m_rotation.queue.size() >= m_rotation.max_size) { @@ -123,17 +125,21 @@ class Mouse3DController if (rotation(0) != 0.0f) ++m_mouse_wheel_counter; #else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_rotation.push(rotation); #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT m_rotation_queue_max_size = std::max(m_rotation_queue_max_size, (unsigned int)m_rotation.size()); #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT if (rotation(0) != 0.0f) ++m_mouse_wheel_counter; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_QUEUE_MAX_SIZE - } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + } void append_button(unsigned int id) { +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_QUEUE_MAX_SIZE while (m_buttons.queue.size() >= m_buttons.max_size) { @@ -144,13 +150,15 @@ class Mouse3DController m_buttons_queue_max_size = std::max(m_buttons_queue_max_size, (unsigned int)m_buttons.queue.size()); #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT #else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_buttons.push(id); #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT m_buttons_queue_max_size = std::max(m_buttons_queue_max_size, (unsigned int)m_buttons.size()); #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT -#endif // ENABLE_QUEUE_MAX_SIZE - } //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_QUEUE_MAX_SIZE +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + } //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_QUEUE_MAX_SIZE @@ -158,9 +166,11 @@ class Mouse3DController bool has_rotation() const { return !m_rotation.queue.empty(); } bool has_button() const { return !m_buttons.queue.empty(); } #else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ bool has_translation() const { return !m_translation.empty(); } bool has_rotation() const { return !m_rotation.empty(); } bool has_button() const { return !m_buttons.empty(); } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_QUEUE_MAX_SIZE //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@ -194,23 +204,25 @@ class Mouse3DController float get_rotation_deadzone() const { return m_rotation_params.deadzone; } void set_rotation_deadzone(float deadzone) { m_rotation_params.deadzone = deadzone; } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_QUEUE_MAX_SIZE unsigned int get_translation_queue_size() const { return (unsigned int)m_translation.queue.size(); } unsigned int get_rotation_queue_size() const { return (unsigned int)m_rotation.queue.size(); } unsigned int get_buttons_queue_size() const { return (unsigned int)m_buttons.queue.size(); } #else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ unsigned int get_translation_queue_size() const { return (unsigned int)m_translation.size(); } unsigned int get_rotation_queue_size() const { return (unsigned int)m_rotation.size(); } unsigned int get_buttons_queue_size() const { return (unsigned int)m_buttons.size(); } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_QUEUE_MAX_SIZE +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ unsigned int get_translation_queue_max_size() const { return m_translation_queue_max_size; } unsigned int get_rotation_queue_max_size() const { return m_rotation_queue_max_size; } unsigned int get_buttons_queue_max_size() const { return m_buttons_queue_max_size; } #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_QUEUE_MAX_SIZE From 7caa596b95dbbdfd2a419dbcf55670b679d5965d Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 10 Oct 2019 11:55:17 +0200 Subject: [PATCH 25/67] ENABLE_3DCONNEXION_DEVICES -> Refactoring and code cleanup --- src/libslic3r/Technologies.hpp | 2 - src/slic3r/GUI/Mouse3DController.cpp | 103 ++++++++-------- src/slic3r/GUI/Mouse3DController.hpp | 171 ++++----------------------- 3 files changed, 72 insertions(+), 204 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index f64bea172..da982c49a 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -39,8 +39,6 @@ // Enabled 3Dconnexion devices #define ENABLE_3DCONNEXION_DEVICES (1 && ENABLE_2_0_0_ALPHA1) -// Enabled 3Dconnexion devices debug output -#define ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT (1 && ENABLE_3DCONNEXION_DEVICES) #endif // _technologies_h_ diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 8cf7be33e..045e42e90 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -72,19 +72,45 @@ Mouse3DController::State::State() { } +void Mouse3DController::State::append_translation(const Vec3d& translation) +{ + while (m_translation.queue.size() >= m_translation.max_size) + { + m_translation.queue.pop(); + } + m_translation.queue.push(translation); +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + m_translation_queue_max_size = std::max(m_translation_queue_max_size, m_translation.queue.size()); +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +} + +void Mouse3DController::State::append_rotation(const Vec3f& rotation) +{ + while (m_rotation.queue.size() >= m_rotation.max_size) + { + m_rotation.queue.pop(); + } + m_rotation.queue.push(rotation); +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + m_rotation_queue_max_size = std::max(m_rotation_queue_max_size, m_rotation.queue.size()); +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + if (rotation(0) != 0.0f) + ++m_mouse_wheel_counter; +} + +void Mouse3DController::State::append_button(unsigned int id) +{ + m_buttons.push(id); +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + m_buttons_queue_max_size = std::max(m_buttons_queue_max_size, m_buttons.size()); +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +} + bool Mouse3DController::State::process_mouse_wheel() { if (m_mouse_wheel_counter == 0) return false; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#if ENABLE_QUEUE_MAX_SIZE else if (!m_rotation.queue.empty()) -#else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - else if (!m_rotation.empty()) -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#endif // ENABLE_QUEUE_MAX_SIZE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ { --m_mouse_wheel_counter; return true; @@ -96,6 +122,21 @@ bool Mouse3DController::State::process_mouse_wheel() } } +void Mouse3DController::State::set_queues_max_size(size_t size) +{ + if (size > 0) + { + m_translation.max_size = size; + m_rotation.max_size = size; + +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + m_translation_queue_max_size = 0; + m_rotation_queue_max_size = 0; + m_buttons_queue_max_size = 0; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + } +} + bool Mouse3DController::State::apply(Camera& camera) { if (!wxGetApp().IsActive()) @@ -105,26 +146,14 @@ bool Mouse3DController::State::apply(Camera& camera) if (has_translation()) { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#if ENABLE_QUEUE_MAX_SIZE const Vec3d& translation = m_translation.queue.front(); camera.set_target(camera.get_target() + m_translation_params.scale * (translation(0) * camera.get_dir_right() + translation(1) * camera.get_dir_forward() + translation(2) * camera.get_dir_up())); m_translation.queue.pop(); -#else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - const Vec3d& translation = m_translation.front(); - camera.set_target(camera.get_target() + m_translation_params.scale * (translation(0) * camera.get_dir_right() + translation(1) * camera.get_dir_forward() + translation(2) * camera.get_dir_up())); - m_translation.pop(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#endif // ENABLE_QUEUE_MAX_SIZE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ret = true; } if (has_rotation()) { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#if ENABLE_QUEUE_MAX_SIZE const Vec3f& rotation = m_rotation.queue.front(); float theta = m_rotation_params.scale * rotation(0); float phi = m_rotation_params.scale * rotation(2); @@ -132,36 +161,11 @@ bool Mouse3DController::State::apply(Camera& camera) camera.phi += sign * phi; camera.set_theta(camera.get_theta() + theta, wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA); m_rotation.queue.pop(); -#else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - const Vec3f& rotation = m_rotation.front(); - float theta = m_rotation_params.scale * rotation(0); - float phi = m_rotation_params.scale * rotation(2); - float sign = camera.inverted_phi ? -1.0f : 1.0f; - camera.phi += sign * phi; - camera.set_theta(camera.get_theta() + theta, wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA); - m_rotation.pop(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#endif // ENABLE_QUEUE_MAX_SIZE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - ret = true; } if (has_button()) { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#if ENABLE_QUEUE_MAX_SIZE - unsigned int button = m_buttons.queue.front(); - switch (button) - { - case 0: { camera.update_zoom(1.0); break; } - case 1: { camera.update_zoom(-1.0); break; } - default: { break; } - } - m_buttons.queue.pop(); -#else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ unsigned int button = m_buttons.front(); switch (button) { @@ -170,9 +174,6 @@ bool Mouse3DController::State::apply(Camera& camera) default: { break; } } m_buttons.pop(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#endif // ENABLE_QUEUE_MAX_SIZE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ret = true; } @@ -310,16 +311,12 @@ void Mouse3DController::render_settings_dialog(unsigned int canvas_width, unsign ImGui::InputInt2("Rotation##4", rotation_size, ImGuiInputTextFlags_ReadOnly); ImGui::InputInt2("Buttons", buttons_size, ImGuiInputTextFlags_ReadOnly); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#if ENABLE_QUEUE_MAX_SIZE int queue_size = (int)m_state.get_queues_max_size(); if (ImGui::InputInt("Max size", &queue_size, 1, 1, ImGuiInputTextFlags_ReadOnly)) { if (queue_size > 0) m_state.set_queues_max_size(queue_size); } -#endif // ENABLE_QUEUE_MAX_SIZE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT imgui.end(); @@ -424,7 +421,7 @@ bool Mouse3DController::connect_device() std::cout << "Manufacturer id/product id....: " << vendor_id << "/" << product_id << std::endl; std::cout << "Manufacturer id/product id hex: " << std::hex << vendor_id << "/" << product_id << std::dec << std::endl; std::cout << "Usage page....................: " << usage_page << std::endl; - std::cout << "Usage.........................: " << usage << std::endl; + std::cout << "Usage.........................: " << usage << std::endl << std::endl; #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT // get device parameters from the config, if present diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index f31bdc913..916983e22 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -2,6 +2,10 @@ #define slic3r_Mouse3DController_hpp_ #if ENABLE_3DCONNEXION_DEVICES + +// Enabled debug output and extended imgui dialog +#define ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT 0 + #include "libslic3r/Point.hpp" #include "hidapi.h" @@ -11,10 +15,6 @@ #include #include -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#define ENABLE_QUEUE_MAX_SIZE 1 -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - namespace Slic3r { namespace GUI { @@ -42,29 +42,21 @@ class Mouse3DController CustomParameters(Number scale, Number deadzone) : scale(scale), deadzone(deadzone) {} }; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#if ENABLE_QUEUE_MAX_SIZE template struct InputQueue { size_t max_size; std::queue queue; - InputQueue() : max_size(20) {} + // The default value of 5 for max_size seems to work fine on all platforms + // The effects of changing this value can be tested by setting ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT to 1 + // and playing with the imgui dialog which shows by pressing CTRL+M + InputQueue() : max_size(5) {} }; InputQueue m_translation; InputQueue m_rotation; - InputQueue m_buttons; -#else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - std::queue m_translation; - std::queue m_rotation; std::queue m_buttons; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#endif // ENABLE_QUEUE_MAX_SIZE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - CustomParameters m_translation_params; CustomParameters m_rotation_params; @@ -79,116 +71,21 @@ class Mouse3DController unsigned int m_mouse_wheel_counter; #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT - unsigned int m_translation_queue_max_size; - unsigned int m_rotation_queue_max_size; - unsigned int m_buttons_queue_max_size; + size_t m_translation_queue_max_size; + size_t m_rotation_queue_max_size; + size_t m_buttons_queue_max_size; #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT public: State(); - void append_translation(const Vec3d& translation) - { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#if ENABLE_QUEUE_MAX_SIZE - while (m_translation.queue.size() >= m_translation.max_size) - { - m_translation.queue.pop(); - } - m_translation.queue.push(translation); -#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT - m_translation_queue_max_size = std::max(m_translation_queue_max_size, (unsigned int)m_translation.queue.size()); -#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT -#else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - m_translation.push(translation); -#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT - m_translation_queue_max_size = std::max(m_translation_queue_max_size, (unsigned int)m_translation.size()); -#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#endif // ENABLE_QUEUE_MAX_SIZE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - } + void append_translation(const Vec3d& translation); + void append_rotation(const Vec3f& rotation); + void append_button(unsigned int id); - void append_rotation(const Vec3f& rotation) - { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#if ENABLE_QUEUE_MAX_SIZE - while (m_rotation.queue.size() >= m_rotation.max_size) - { - m_rotation.queue.pop(); - } - m_rotation.queue.push(rotation); -#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT - m_rotation_queue_max_size = std::max(m_rotation_queue_max_size, (unsigned int)m_rotation.queue.size()); -#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT - if (rotation(0) != 0.0f) - ++m_mouse_wheel_counter; -#else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - m_rotation.push(rotation); -#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT - m_rotation_queue_max_size = std::max(m_rotation_queue_max_size, (unsigned int)m_rotation.size()); -#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT - if (rotation(0) != 0.0f) - ++m_mouse_wheel_counter; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#endif // ENABLE_QUEUE_MAX_SIZE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - } - - void append_button(unsigned int id) - { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#if ENABLE_QUEUE_MAX_SIZE - while (m_buttons.queue.size() >= m_buttons.max_size) - { - m_buttons.queue.pop(); - } - m_buttons.queue.push(id); -#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT - m_buttons_queue_max_size = std::max(m_buttons_queue_max_size, (unsigned int)m_buttons.queue.size()); -#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT -#else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - m_buttons.push(id); -#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT - m_buttons_queue_max_size = std::max(m_buttons_queue_max_size, (unsigned int)m_buttons.size()); -#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#endif // ENABLE_QUEUE_MAX_SIZE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - } - -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#if ENABLE_QUEUE_MAX_SIZE bool has_translation() const { return !m_translation.queue.empty(); } bool has_rotation() const { return !m_rotation.queue.empty(); } - bool has_button() const { return !m_buttons.queue.empty(); } -#else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - bool has_translation() const { return !m_translation.empty(); } - bool has_rotation() const { return !m_rotation.empty(); } bool has_button() const { return !m_buttons.empty(); } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#endif // ENABLE_QUEUE_MAX_SIZE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - -#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#if ENABLE_QUEUE_MAX_SIZE - Vec3d get_translation() const { return has_translation() ? m_translation.queue.front() : Vec3d::Zero(); } - Vec3f get_rotation() const { return has_rotation() ? m_rotation.queue.front() : Vec3f::Zero(); } - unsigned int get_button() const { return has_button() ? m_buttons.queue.front() : 0; } -#else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - Vec3d get_translation() const { return has_translation() ? m_translation.front() : Vec3d::Zero(); } - Vec3f get_rotation() const { return has_rotation() ? m_rotation.front() : Vec3f::Zero(); } - unsigned int get_button() const { return has_button() ? m_buttons.front() : 0; } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#endif // ENABLE_QUEUE_MAX_SIZE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT bool process_mouse_wheel(); @@ -205,45 +102,21 @@ class Mouse3DController void set_rotation_deadzone(float deadzone) { m_rotation_params.deadzone = deadzone; } #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#if ENABLE_QUEUE_MAX_SIZE + Vec3d get_translation() const { return has_translation() ? m_translation.queue.front() : Vec3d::Zero(); } + Vec3f get_rotation() const { return has_rotation() ? m_rotation.queue.front() : Vec3f::Zero(); } + unsigned int get_button() const { return has_button() ? m_buttons.front() : 0; } + unsigned int get_translation_queue_size() const { return (unsigned int)m_translation.queue.size(); } unsigned int get_rotation_queue_size() const { return (unsigned int)m_rotation.queue.size(); } - unsigned int get_buttons_queue_size() const { return (unsigned int)m_buttons.queue.size(); } -#else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - unsigned int get_translation_queue_size() const { return (unsigned int)m_translation.size(); } - unsigned int get_rotation_queue_size() const { return (unsigned int)m_rotation.size(); } unsigned int get_buttons_queue_size() const { return (unsigned int)m_buttons.size(); } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#endif // ENABLE_QUEUE_MAX_SIZE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - unsigned int get_translation_queue_max_size() const { return m_translation_queue_max_size; } - unsigned int get_rotation_queue_max_size() const { return m_rotation_queue_max_size; } - unsigned int get_buttons_queue_max_size() const { return m_buttons_queue_max_size; } + size_t get_translation_queue_max_size() const { return m_translation_queue_max_size; } + size_t get_rotation_queue_max_size() const { return m_rotation_queue_max_size; } + size_t get_buttons_queue_max_size() const { return m_buttons_queue_max_size; } #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#if ENABLE_QUEUE_MAX_SIZE size_t get_queues_max_size() const { return m_translation.max_size; } - void set_queues_max_size(size_t size) - { - if (size > 0) - { - std::cout << "New queues max size: " << size << std::endl; - - m_translation.max_size = size; - m_rotation.max_size = size; - m_buttons.max_size = size; - - m_translation_queue_max_size = 0; - m_rotation_queue_max_size = 0; - m_buttons_queue_max_size = 0; - } - } -#endif // ENABLE_QUEUE_MAX_SIZE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + void set_queues_max_size(size_t size); // return true if any change to the camera took place bool apply(Camera& camera); From a7972bcc77733c0ae1d11ddbbb02e8c75dd9488d Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 11 Oct 2019 09:16:20 +0200 Subject: [PATCH 26/67] ENABLE_3DCONNEXION_DEVICES -> Debug output of device features --- src/slic3r/GUI/Mouse3DController.cpp | 34 ++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 045e42e90..7e3be9047 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -115,11 +115,9 @@ bool Mouse3DController::State::process_mouse_wheel() --m_mouse_wheel_counter; return true; } - else - { - m_mouse_wheel_counter = 0; - return true; - } + + m_mouse_wheel_counter = 0; + return true; } void Mouse3DController::State::set_queues_max_size(size_t size) @@ -422,6 +420,22 @@ bool Mouse3DController::connect_device() std::cout << "Manufacturer id/product id hex: " << std::hex << vendor_id << "/" << product_id << std::dec << std::endl; std::cout << "Usage page....................: " << usage_page << std::endl; std::cout << "Usage.........................: " << usage << std::endl << std::endl; + + std::cout << "Feature reports:" << std::endl; + for (int i = 0; i < 256; ++i) + { + DataPacket packet = { 0 }; + packet[0] = (unsigned char)i; + int res = hid_get_feature_report(m_device, packet.data(), packet.size()); + if (res > 0) + { + for (int j = 0; j < res; ++j) + { + std::cout << " " << (int)packet[j]; + } + std::cout << std::endl; + } + } #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT // get device parameters from the config, if present @@ -434,9 +448,9 @@ bool Mouse3DController::connect_device() wxGetApp().app_config->get_mouse_device_rotation_speed(m_device_str, rotation_speed); wxGetApp().app_config->get_mouse_device_rotation_deadzone(m_device_str, rotation_deadzone); // clamp to valid values - m_state.set_translation_scale(State::DefaultTranslationScale* std::max(0.5, std::min(2.0, translation_speed))); + m_state.set_translation_scale(State::DefaultTranslationScale * std::max(0.5, std::min(2.0, translation_speed))); m_state.set_translation_deadzone(std::max(0.0, std::min(State::MaxTranslationDeadzone, translation_deadzone))); - m_state.set_rotation_scale(State::DefaultRotationScale* std::max(0.5f, std::min(2.0f, rotation_speed))); + m_state.set_rotation_scale(State::DefaultRotationScale * std::max(0.5f, std::min(2.0f, rotation_speed))); m_state.set_rotation_deadzone(std::max(0.0f, std::min(State::MaxRotationDeadzone, rotation_deadzone))); } @@ -505,8 +519,10 @@ void Mouse3DController::collect_input() updated = handle_packet(packet); else if (res == 13) updated = handle_wireless_packet(packet); +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT else if (res > 0) std::cout << "Got unknown data packet of length: " << res << ", code:" << (int)packet[0] << std::endl; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT if (updated) // ask for an idle event to update 3D scene @@ -540,7 +556,9 @@ bool Mouse3DController::handle_packet(const DataPacket& packet) } default: { +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT std::cout << "Got unknown data packet of code: " << (int)packet[0] << std::endl; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT break; } } @@ -571,7 +589,9 @@ bool Mouse3DController::handle_wireless_packet(const DataPacket& packet) } default: { +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT std::cout << "Got unknown data packet of code: " << (int)packet[0] << std::endl; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT break; } } From 1c05d88c008c9d2b27cc43722e322c8df5fd9b05 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 11 Oct 2019 15:29:57 +0200 Subject: [PATCH 27/67] ENABLE_3DCONNEXION_DEVICES -> Added support for 3Dconnexion universal receiver and battery level report --- resources/udev/90-3dconnexion.rules | 42 ++++++++++++++++----- src/slic3r/GUI/Mouse3DController.cpp | 55 ++++++++++++---------------- 2 files changed, 55 insertions(+), 42 deletions(-) diff --git a/resources/udev/90-3dconnexion.rules b/resources/udev/90-3dconnexion.rules index 035d17125..670d91790 100644 --- a/resources/udev/90-3dconnexion.rules +++ b/resources/udev/90-3dconnexion.rules @@ -1,23 +1,45 @@ # See src/slic3r/GUI/Mouse3DController.cpp for the list of devices # Logitech vendor devices -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c623", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c626", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c628", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c627", MODE="0666" SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c603", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c62b", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c605", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c606", MODE="0666" SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c621", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c623", MODE="0666" SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c625", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c626", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c627", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c628", MODE="0666" SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c629", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c62b", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c62e", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c62f", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c631", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c632", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c633", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c635", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c636", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c640", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c652", MODE="0666" # 3D Connexion vendor devices -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c623", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c626", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c628", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c627", MODE="0666" SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c603", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c62b", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c605", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c606", MODE="0666" SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c621", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c623", MODE="0666" SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c625", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c626", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c627", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c628", MODE="0666" SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c629", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c62b", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c62e", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c62f", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c631", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c632", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c633", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c635", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c636", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c640", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c652", MODE="0666" diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 7e3be9047..d75ad4390 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -46,8 +46,7 @@ static const std::vector _3DCONNEXION_DEVICES = 0xc635, /* 50741 spacemouse compact *TESTED* */ 0xc636, /* 50742 spacemouse module */ 0xc640, /* 50752 nulooq */ - -// 0xc652, /* 50770 3Dconnexion universal receiver */ + 0xc652, /* 50770 3Dconnexion universal receiver *TESTED* */ }; namespace Slic3r { @@ -336,12 +335,11 @@ bool Mouse3DController::connect_device() } // Searches for 1st connected 3Dconnexion device + std::string path = ""; unsigned short vendor_id = 0; unsigned short product_id = 0; #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT - unsigned short usage_page = 0; - unsigned short usage = 0; hid_device_info* cur = devices; while (cur != nullptr) { @@ -371,13 +369,10 @@ bool Mouse3DController::connect_device() { for (size_t i = 0; i < _3DCONNEXION_DEVICES.size(); ++i) { - if (_3DCONNEXION_DEVICES[i] == current->product_id) + if ((_3DCONNEXION_DEVICES[i] == current->product_id) && (current->usage_page == 1) && (current->usage == 8)) { product_id = current->product_id; -#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT - usage_page = current->usage_page; - usage = current->usage; -#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + path = current->path; break; } } @@ -398,8 +393,8 @@ bool Mouse3DController::connect_device() if (vendor_id == 0) return false; - // Open the 3Dconnexion device using the VID, PID - m_device = hid_open(vendor_id, product_id, nullptr); + // Open the 3Dconnexion device using the device path + m_device = hid_open_path(path.c_str()); if (m_device != nullptr) { @@ -415,27 +410,9 @@ bool Mouse3DController::connect_device() #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT std::cout << std::endl << "Connected device:" << std::endl; - std::cout << "Manufacturer/product..........: " << m_device_str << std::endl; - std::cout << "Manufacturer id/product id....: " << vendor_id << "/" << product_id << std::endl; - std::cout << "Manufacturer id/product id hex: " << std::hex << vendor_id << "/" << product_id << std::dec << std::endl; - std::cout << "Usage page....................: " << usage_page << std::endl; - std::cout << "Usage.........................: " << usage << std::endl << std::endl; - - std::cout << "Feature reports:" << std::endl; - for (int i = 0; i < 256; ++i) - { - DataPacket packet = { 0 }; - packet[0] = (unsigned char)i; - int res = hid_get_feature_report(m_device, packet.data(), packet.size()); - if (res > 0) - { - for (int j = 0; j < res; ++j) - { - std::cout << " " << (int)packet[j]; - } - std::cout << std::endl; - } - } + std::cout << "Manufacturer/product: " << m_device_str << std::endl; + std::cout << "Manufacturer id.....: " << vendor_id << " (" << std::hex << vendor_id << std::dec << ")" << std::endl; + std::cout << "Product id..........: " << product_id << " (" << std::hex << product_id << std::dec << ")" << std::endl; #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT // get device parameters from the config, if present @@ -552,6 +529,13 @@ bool Mouse3DController::handle_packet(const DataPacket& packet) if (handle_packet_button(packet, 6)) return true; + break; + } + case 23: // Battery charge + { +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + std::cout << m_device_str << " - battery level: " << (int)packet[1] << " percent" << std::endl; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT break; } default: @@ -585,6 +569,13 @@ bool Mouse3DController::handle_wireless_packet(const DataPacket& packet) if (handle_packet_button(packet, 12)) return true; + break; + } + case 23: // Battery charge + { +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + std::cout << m_device_str << " - battery level: " << (int)packet[1] << " percent" << std::endl; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT break; } default: From e6403a74efaa15472689f460bd9a754d6129c348 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 11 Oct 2019 15:51:36 +0200 Subject: [PATCH 28/67] ENABLE_3DCONNEXION_DEVICES tech set as default --- src/libslic3r/Technologies.hpp | 9 ----- src/slic3r/GUI/AppConfig.cpp | 2 -- src/slic3r/GUI/AppConfig.hpp | 2 -- src/slic3r/GUI/Camera.cpp | 25 -------------- src/slic3r/GUI/Camera.hpp | 5 --- src/slic3r/GUI/GLCanvas3D.cpp | 51 ---------------------------- src/slic3r/GUI/GLCanvas3D.hpp | 8 ----- src/slic3r/GUI/ImGuiWrapper.cpp | 2 -- src/slic3r/GUI/KBShortcutsDialog.cpp | 2 -- src/slic3r/GUI/MainFrame.cpp | 2 -- src/slic3r/GUI/Mouse3DController.cpp | 4 --- src/slic3r/GUI/Mouse3DController.hpp | 6 +--- src/slic3r/GUI/Plater.cpp | 10 ------ src/slic3r/GUI/Plater.hpp | 2 -- 14 files changed, 1 insertion(+), 129 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index da982c49a..51d092094 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -32,13 +32,4 @@ #define ENABLE_NONCUSTOM_DATA_VIEW_RENDERING (0 && ENABLE_1_42_0_ALPHA1) -//==================== -// 2.0.0.alpha1 techs -//==================== -#define ENABLE_2_0_0_ALPHA1 1 - -// Enabled 3Dconnexion devices -#define ENABLE_3DCONNEXION_DEVICES (1 && ENABLE_2_0_0_ALPHA1) - - #endif // _technologies_h_ diff --git a/src/slic3r/GUI/AppConfig.cpp b/src/slic3r/GUI/AppConfig.cpp index c29fe1c3b..60f4edf47 100644 --- a/src/slic3r/GUI/AppConfig.cpp +++ b/src/slic3r/GUI/AppConfig.cpp @@ -271,7 +271,6 @@ void AppConfig::set_recent_projects(const std::vector& recent_proje } } -#if ENABLE_3DCONNEXION_DEVICES void AppConfig::set_mouse_device(const std::string& name, double translation_speed, double translation_deadzone, float rotation_speed, float rotation_deadzone) { std::string key = std::string("mouse_device:") + name; @@ -345,7 +344,6 @@ bool AppConfig::get_mouse_device_rotation_deadzone(const std::string& name, floa deadzone = (float)::atof(it_val->second.c_str()); return true; } -#endif // ENABLE_3DCONNEXION_DEVICES void AppConfig::update_config_dir(const std::string &dir) { diff --git a/src/slic3r/GUI/AppConfig.hpp b/src/slic3r/GUI/AppConfig.hpp index fce0c0b39..355370450 100644 --- a/src/slic3r/GUI/AppConfig.hpp +++ b/src/slic3r/GUI/AppConfig.hpp @@ -131,13 +131,11 @@ public: std::vector get_recent_projects() const; void set_recent_projects(const std::vector& recent_projects); -#if ENABLE_3DCONNEXION_DEVICES void set_mouse_device(const std::string& name, double translation_speed, double translation_deadzone, float rotation_speed, float rotation_deadzone); bool get_mouse_device_translation_speed(const std::string& name, double& speed); bool get_mouse_device_translation_deadzone(const std::string& name, double& deadzone); bool get_mouse_device_rotation_speed(const std::string& name, float& speed); bool get_mouse_device_rotation_deadzone(const std::string& name, float& deadzone); -#endif // ENABLE_3DCONNEXION_DEVICES static const std::string SECTION_FILAMENTS; static const std::string SECTION_MATERIALS; diff --git a/src/slic3r/GUI/Camera.cpp b/src/slic3r/GUI/Camera.cpp index e43b57636..9ecb5b4ad 100644 --- a/src/slic3r/GUI/Camera.cpp +++ b/src/slic3r/GUI/Camera.cpp @@ -85,7 +85,6 @@ void Camera::select_next_type() void Camera::set_target(const Vec3d& target) { -#if ENABLE_3DCONNEXION_DEVICES // We may let these factors be customizable static const double ScaleFactor = 1.1; BoundingBoxf3 test_box = m_scene_box; @@ -94,12 +93,6 @@ void Camera::set_target(const Vec3d& target) m_target(0) = clamp(test_box.min(0), test_box.max(0), m_target(0)); m_target(1) = clamp(test_box.min(1), test_box.max(1), m_target(1)); m_target(2) = clamp(test_box.min(2), test_box.max(2), m_target(2)); -#else - m_target = target; - m_target(0) = clamp(m_scene_box.min(0), m_scene_box.max(0), m_target(0)); - m_target(1) = clamp(m_scene_box.min(1), m_scene_box.max(1), m_target(1)); - m_target(2) = clamp(m_scene_box.min(2), m_scene_box.max(2), m_target(2)); -#endif // ENABLE_3DCONNEXION_DEVICES } void Camera::set_theta(float theta, bool apply_limit) @@ -114,7 +107,6 @@ void Camera::set_theta(float theta, bool apply_limit) } } -#if ENABLE_3DCONNEXION_DEVICES void Camera::update_zoom(double delta_zoom) { set_zoom(m_zoom / (1.0 - std::max(std::min(delta_zoom, 4.0), -4.0) * 0.1)); @@ -130,23 +122,6 @@ void Camera::set_zoom(double zoom) // Don't allow to zoom too close to the scene. m_zoom = std::min(zoom, 100.0); } -#else -void Camera::set_zoom(double zoom, const BoundingBoxf3& max_box, int canvas_w, int canvas_h) -{ - zoom = std::max(std::min(zoom, 4.0), -4.0) / 10.0; - zoom = m_zoom / (1.0 - zoom); - - // Don't allow to zoom too far outside the scene. - double zoom_min = calc_zoom_to_bounding_box_factor(max_box, canvas_w, canvas_h); - if (zoom_min > 0.0) - zoom = std::max(zoom, zoom_min * 0.7); - - // Don't allow to zoom too close to the scene. - zoom = std::min(zoom, 100.0); - - m_zoom = zoom; -} -#endif // ENABLE_3DCONNEXION_DEVICES bool Camera::select_view(const std::string& direction) { diff --git a/src/slic3r/GUI/Camera.hpp b/src/slic3r/GUI/Camera.hpp index bf452833c..fae203a28 100644 --- a/src/slic3r/GUI/Camera.hpp +++ b/src/slic3r/GUI/Camera.hpp @@ -63,13 +63,8 @@ public: void set_theta(float theta, bool apply_limit); double get_zoom() const { return m_zoom; } -#if ENABLE_3DCONNEXION_DEVICES void update_zoom(double delta_zoom); void set_zoom(double zoom); -#else - void set_zoom(double zoom, const BoundingBoxf3& max_box, int canvas_w, int canvas_h); - void set_zoom(double zoom) { m_zoom = zoom; } -#endif // ENABLE_3DCONNEXION_DEVICES const BoundingBoxf3& get_scene_box() const { return m_scene_box; } void set_scene_box(const BoundingBoxf3& box) { m_scene_box = box; } diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index af0daef6d..8610db3fb 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -22,9 +22,7 @@ #include "GUI_App.hpp" #include "GUI_ObjectList.hpp" #include "GUI_ObjectManipulation.hpp" -#if ENABLE_3DCONNEXION_DEVICES #include "Mouse3DController.hpp" -#endif // ENABLE_3DCONNEXION_DEVICES #include "I18N.hpp" #if ENABLE_RETINA_GL @@ -1394,11 +1392,7 @@ BoundingBoxf3 GLCanvas3D::volumes_bounding_box() const BoundingBoxf3 GLCanvas3D::scene_bounding_box() const { BoundingBoxf3 bb = volumes_bounding_box(); -#if ENABLE_3DCONNEXION_DEVICES bb.merge(m_bed.get_bounding_box(true)); -#else - bb.merge(m_bed.get_bounding_box(false)); -#endif // ENABLE_3DCONNEXION_DEVICES if (m_config != nullptr) { @@ -1546,16 +1540,11 @@ void GLCanvas3D::render() return; } -#if ENABLE_3DCONNEXION_DEVICES const Size& cnv_size = get_canvas_size(); -#endif // ENABLE_3DCONNEXION_DEVICES if (m_camera.requires_zoom_to_bed) { zoom_to_bed(); -#if !ENABLE_3DCONNEXION_DEVICES - const Size& cnv_size = get_canvas_size(); -#endif // !ENABLE_3DCONNEXION_DEVICES _resize((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height()); m_camera.requires_zoom_to_bed = false; } @@ -1646,9 +1635,7 @@ void GLCanvas3D::render() m_camera.debug_render(); #endif // ENABLE_CAMERA_STATISTICS -#if ENABLE_3DCONNEXION_DEVICES wxGetApp().plater()->get_mouse3d_controller().render_settings_dialog((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height()); -#endif // ENABLE_3DCONNEXION_DEVICES wxGetApp().imgui()->render(); @@ -2350,17 +2337,14 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt) m_dirty |= m_main_toolbar.update_items_state(); m_dirty |= m_undoredo_toolbar.update_items_state(); m_dirty |= m_view_toolbar.update_items_state(); -#if ENABLE_3DCONNEXION_DEVICES bool mouse3d_controller_applied = wxGetApp().plater()->get_mouse3d_controller().apply(m_camera); m_dirty |= mouse3d_controller_applied; -#endif // ENABLE_3DCONNEXION_DEVICES if (!m_dirty) return; _refresh_if_shown_on_screen(); -#if ENABLE_3DCONNEXION_DEVICES if (m_keep_dirty || mouse3d_controller_applied) { m_dirty = true; @@ -2368,10 +2352,6 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt) } else m_dirty = false; -#else - if (m_keep_dirty) - m_dirty = true; -#endif // ENABLE_3DCONNEXION_DEVICES } void GLCanvas3D::on_char(wxKeyEvent& evt) @@ -2417,7 +2397,6 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) post_event(SimpleEvent(EVT_GLTOOLBAR_COPY)); break; -#if ENABLE_3DCONNEXION_DEVICES #ifdef __APPLE__ case 'm': case 'M': @@ -2430,7 +2409,6 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) m_dirty = true; break; } -#endif // ENABLE_3DCONNEXION_DEVICES #ifdef __APPLE__ case 'v': @@ -2499,19 +2477,11 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) case 'B': case 'b': { zoom_to_bed(); break; } case 'I': -#if ENABLE_3DCONNEXION_DEVICES case 'i': { _update_camera_zoom(1.0); break; } -#else - case 'i': { set_camera_zoom(1.0); break; } -#endif // ENABLE_3DCONNEXION_DEVICES case 'K': case 'k': { m_camera.select_next_type(); m_dirty = true; break; } case 'O': -#if ENABLE_3DCONNEXION_DEVICES case 'o': { _update_camera_zoom(-1.0); break; } -#else - case 'o': { set_camera_zoom(-1.0); break; } -#endif // ENABLE_3DCONNEXION_DEVICES #if ENABLE_RENDER_PICKING_PASS case 'T': case 't': { @@ -2614,12 +2584,10 @@ void GLCanvas3D::on_key(wxKeyEvent& evt) void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt) { -#if ENABLE_3DCONNEXION_DEVICES // try to filter out events coming from mouse 3d Mouse3DController& controller = wxGetApp().plater()->get_mouse3d_controller(); if (controller.process_mouse_wheel()) return; -#endif // ENABLE_3DCONNEXION_DEVICES if (!m_initialized) return; @@ -2665,11 +2633,7 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt) return; // Calculate the zoom delta and apply it to the current zoom factor -#if ENABLE_3DCONNEXION_DEVICES _update_camera_zoom((double)evt.GetWheelRotation() / (double)evt.GetWheelDelta()); -#else - set_camera_zoom((double)evt.GetWheelRotation() / (double)evt.GetWheelDelta()); -#endif // ENABLE_3DCONNEXION_DEVICES } void GLCanvas3D::on_timer(wxTimerEvent& evt) @@ -3451,15 +3415,6 @@ void GLCanvas3D::do_mirror(const std::string& snapshot_type) m_dirty = true; } -#if !ENABLE_3DCONNEXION_DEVICES -void GLCanvas3D::set_camera_zoom(double zoom) -{ - const Size& cnv_size = get_canvas_size(); - m_camera.set_zoom(zoom, _max_bounding_box(false, true), cnv_size.get_width(), cnv_size.get_height()); - m_dirty = true; -} -#endif // !ENABLE_3DCONNEXION_DEVICES - void GLCanvas3D::update_gizmos_on_off_state() { set_as_dirty(); @@ -3910,10 +3865,6 @@ void GLCanvas3D::_resize(unsigned int w, unsigned int h) // updates camera m_camera.apply_viewport(0, 0, w, h); - -#if !ENABLE_3DCONNEXION_DEVICES - m_dirty = false; -#endif // !ENABLE_3DCONNEXION_DEVICES } BoundingBoxf3 GLCanvas3D::_max_bounding_box(bool include_gizmos, bool include_bed_model) const @@ -3941,13 +3892,11 @@ void GLCanvas3D::_zoom_to_box(const BoundingBoxf3& box) m_dirty = true; } -#if ENABLE_3DCONNEXION_DEVICES void GLCanvas3D::_update_camera_zoom(double zoom) { m_camera.update_zoom(zoom); m_dirty = true; } -#endif // ENABLE_3DCONNEXION_DEVICES void GLCanvas3D::_refresh_if_shown_on_screen() { diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 5c3c26c1d..2338ebc90 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -486,9 +486,7 @@ public: void set_color_by(const std::string& value); const Camera& get_camera() const { return m_camera; } -#if ENABLE_3DCONNEXION_DEVICES Camera& get_camera() { return m_camera; } -#endif // ENABLE_3DCONNEXION_DEVICES BoundingBoxf3 volumes_bounding_box() const; BoundingBoxf3 scene_bounding_box() const; @@ -567,10 +565,6 @@ public: void do_flatten(const Vec3d& normal, const std::string& snapshot_type); void do_mirror(const std::string& snapshot_type); -#if !ENABLE_3DCONNEXION_DEVICES - void set_camera_zoom(double zoom); -#endif // !ENABLE_3DCONNEXION_DEVICES - void update_gizmos_on_off_state(); void reset_all_gizmos() { m_gizmos.reset_all_states(); } @@ -644,9 +638,7 @@ private: BoundingBoxf3 _max_bounding_box(bool include_gizmos, bool include_bed_model) const; void _zoom_to_box(const BoundingBoxf3& box); -#if ENABLE_3DCONNEXION_DEVICES void _update_camera_zoom(double zoom); -#endif // ENABLE_3DCONNEXION_DEVICES void _refresh_if_shown_on_screen(); diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 7d7e82685..9dfe39bdd 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -529,10 +529,8 @@ void ImGuiWrapper::init_style() set_color(ImGuiCol_SliderGrab, COL_ORANGE_DARK); set_color(ImGuiCol_SliderGrabActive, COL_ORANGE_LIGHT); -#if ENABLE_3DCONNEXION_DEVICES // Separator set_color(ImGuiCol_Separator, COL_ORANGE_LIGHT); -#endif // ENABLE_3DCONNEXION_DEVICES } void ImGuiWrapper::render_draw_data(ImDrawData *draw_data) diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index 4bd56ee76..268682b81 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -157,9 +157,7 @@ void KBShortcutsDialog::fill_shortcuts() plater_shortcuts.push_back(Shortcut("Z", L("Zoom to selected object"))); plater_shortcuts.push_back(Shortcut("I", L("Zoom in"))); plater_shortcuts.push_back(Shortcut("O", L("Zoom out"))); -#if ENABLE_3DCONNEXION_DEVICES plater_shortcuts.push_back(Shortcut(ctrl+"M", L("Show/Hide 3Dconnexion devices settings dialog"))); -#endif // ENABLE_3DCONNEXION_DEVICES plater_shortcuts.push_back(Shortcut("ESC", L("Unselect gizmo / Clear selection"))); #if ENABLE_RENDER_PICKING_PASS // Don't localize debugging texts. diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 8be99b6a6..3b5b1003f 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -24,9 +24,7 @@ #include "PrintHostDialogs.hpp" #include "wxExtensions.hpp" #include "GUI_ObjectList.hpp" -#if ENABLE_3DCONNEXION_DEVICES #include "Mouse3DController.hpp" -#endif // ENABLE_3DCONNEXION_DEVICES #include "I18N.hpp" #include diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index d75ad4390..631844c5c 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -1,8 +1,6 @@ #include "libslic3r/libslic3r.h" #include "Mouse3DController.hpp" -#if ENABLE_3DCONNEXION_DEVICES - #include "Camera.hpp" #include "GUI_App.hpp" #include "PresetBundle.hpp" @@ -647,5 +645,3 @@ bool Mouse3DController::handle_packet_button(const DataPacket& packet, unsigned } // namespace GUI } // namespace Slic3r - -#endif // ENABLE_3DCONNEXION_DEVICES diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index 916983e22..5530328b7 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -1,9 +1,7 @@ #ifndef slic3r_Mouse3DController_hpp_ #define slic3r_Mouse3DController_hpp_ -#if ENABLE_3DCONNEXION_DEVICES - -// Enabled debug output and extended imgui dialog +// Enabled debug output to console and extended imgui dialog #define ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT 0 #include "libslic3r/Point.hpp" @@ -169,7 +167,5 @@ private: } // namespace GUI } // namespace Slic3r -#endif // ENABLE_3DCONNEXION_DEVICES - #endif // slic3r_Mouse3DController_hpp_ diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index cfe180248..f75dde498 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -62,9 +62,7 @@ #include "GUI_Preview.hpp" #include "3DBed.hpp" #include "Camera.hpp" -#if ENABLE_3DCONNEXION_DEVICES #include "Mouse3DController.hpp" -#endif // ENABLE_3DCONNEXION_DEVICES #include "Tab.hpp" #include "PresetBundle.hpp" #include "BackgroundSlicingProcess.hpp" @@ -1373,9 +1371,7 @@ struct Plater::priv Sidebar *sidebar; Bed3D bed; Camera camera; -#if ENABLE_3DCONNEXION_DEVICES Mouse3DController mouse3d_controller; -#endif // ENABLE_3DCONNEXION_DEVICES View3D* view3D; GLToolbar view_toolbar; Preview *preview; @@ -2107,9 +2103,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) // updates camera type from .ini file camera.set_type(get_config("use_perspective_camera")); -#if ENABLE_3DCONNEXION_DEVICES mouse3d_controller.init(); -#endif // ENABLE_3DCONNEXION_DEVICES // Initialize the Undo / Redo stack with a first snapshot. this->take_snapshot(_(L("New Project"))); @@ -2117,9 +2111,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) Plater::priv::~priv() { -#if ENABLE_3DCONNEXION_DEVICES mouse3d_controller.shutdown(); -#endif // ENABLE_3DCONNEXION_DEVICES if (config != nullptr) delete config; @@ -5159,7 +5151,6 @@ const Camera& Plater::get_camera() const return p->camera; } -#if ENABLE_3DCONNEXION_DEVICES const Mouse3DController& Plater::get_mouse3d_controller() const { return p->mouse3d_controller; @@ -5169,7 +5160,6 @@ Mouse3DController& Plater::get_mouse3d_controller() { return p->mouse3d_controller; } -#endif // ENABLE_3DCONNEXION_DEVICES bool Plater::can_delete() const { return p->can_delete(); } bool Plater::can_delete_all() const { return p->can_delete_all(); } diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index a87ba39b4..c327b4d91 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -260,10 +260,8 @@ public: void msw_rescale(); const Camera& get_camera() const; -#if ENABLE_3DCONNEXION_DEVICES const Mouse3DController& get_mouse3d_controller() const; Mouse3DController& get_mouse3d_controller(); -#endif // ENABLE_3DCONNEXION_DEVICES // ROII wrapper for suppressing the Undo / Redo snapshot to be taken. class SuppressSnapshots From a065c923029e104097c6145d4313327a5f2cb633 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 21 Oct 2019 11:06:18 +0200 Subject: [PATCH 29/67] Mouse3DController -> Enabled debug output to check device connection on Linux and Max --- src/slic3r/GUI/Mouse3DController.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 631844c5c..80bc6eb6c 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -345,7 +345,8 @@ bool Mouse3DController::connect_device() std::wcout << ((cur->manufacturer_string != nullptr) ? cur->manufacturer_string : L"Unknown"); std::cout << "::"; std::wcout << ((cur->product_string != nullptr) ? cur->product_string : L"Unknown"); - std::cout << "' code: " << cur->vendor_id << "/" << cur->product_id << " (" << std::hex << cur->vendor_id << "/" << cur->product_id << std::dec << ")" << std::endl; + std::cout << "' code: " << cur->vendor_id << "/" << cur->product_id << " (" << std::hex << cur->vendor_id << "/" << cur->product_id << std::dec << ")"; + std::cout << " usage page: " << cur->usage_page << " usage: " << cur->usage << std::endl; cur = cur->next; } @@ -411,6 +412,7 @@ bool Mouse3DController::connect_device() std::cout << "Manufacturer/product: " << m_device_str << std::endl; std::cout << "Manufacturer id.....: " << vendor_id << " (" << std::hex << vendor_id << std::dec << ")" << std::endl; std::cout << "Product id..........: " << product_id << " (" << std::hex << product_id << std::dec << ")" << std::endl; + std::cout << "Path................: '" << path << "'" << std::endl; #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT // get device parameters from the config, if present @@ -428,6 +430,16 @@ bool Mouse3DController::connect_device() m_state.set_rotation_scale(State::DefaultRotationScale * std::max(0.5f, std::min(2.0f, rotation_speed))); m_state.set_rotation_deadzone(std::max(0.0f, std::min(State::MaxRotationDeadzone, rotation_deadzone))); } +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + else + { + std::cout << std::endl << "Unable to connect to device:" << std::endl; + std::cout << "Manufacturer/product: " << m_device_str << std::endl; + std::cout << "Manufacturer id.....: " << vendor_id << " (" << std::hex << vendor_id << std::dec << ")" << std::endl; + std::cout << "Product id..........: " << product_id << " (" << std::hex << product_id << std::dec << ")" << std::endl; + std::cout << "Path................: '" << path << "'" << std::endl; + } +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT return (m_device != nullptr); } From 76a2f72e972ebe4190e723c6113b8425fe758a07 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 21 Oct 2019 11:12:56 +0200 Subject: [PATCH 30/67] Follow-up of a065c923029e104097c6145d4313327a5f2cb633 -> proper enabling of debug output --- src/slic3r/GUI/Mouse3DController.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index 5530328b7..9812cb742 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -2,7 +2,7 @@ #define slic3r_Mouse3DController_hpp_ // Enabled debug output to console and extended imgui dialog -#define ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT 0 +#define ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT 1 #include "libslic3r/Point.hpp" From c47ca5a2c03c1f933cf151dd47f54d1e14cc0d89 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 21 Oct 2019 11:30:29 +0200 Subject: [PATCH 31/67] Mouse3DController -> Added debug output of device serial number --- src/slic3r/GUI/Mouse3DController.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 80bc6eb6c..0143b3928 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -346,6 +346,8 @@ bool Mouse3DController::connect_device() std::cout << "::"; std::wcout << ((cur->product_string != nullptr) ? cur->product_string : L"Unknown"); std::cout << "' code: " << cur->vendor_id << "/" << cur->product_id << " (" << std::hex << cur->vendor_id << "/" << cur->product_id << std::dec << ")"; + std::cout << " serial number: "; + std::wcout << ((cur->serial_number != nullptr) ? cur->serial_number : L"Unknown"); std::cout << " usage page: " << cur->usage_page << " usage: " << cur->usage << std::endl; cur = cur->next; From 3ce3f33c9cfce023af856998ab9619641f69917d Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 21 Oct 2019 14:21:51 +0200 Subject: [PATCH 32/67] Mouse3DController::connect_device() -> Modified logic to detect and select plugged device --- src/slic3r/GUI/Mouse3DController.cpp | 84 +++++++++++++++++++++++----- 1 file changed, 69 insertions(+), 15 deletions(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 0143b3928..fae62ca7e 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -333,9 +333,13 @@ bool Mouse3DController::connect_device() } // Searches for 1st connected 3Dconnexion device - std::string path = ""; - unsigned short vendor_id = 0; - unsigned short product_id = 0; + struct DeviceData + { + std::string path{ "" }; + int interface_number{ 0 }; + unsigned short usage_page{ 0 }; + unsigned short usage{ 0 }; + }; #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT hid_device_info* cur = devices; @@ -346,17 +350,32 @@ bool Mouse3DController::connect_device() std::cout << "::"; std::wcout << ((cur->product_string != nullptr) ? cur->product_string : L"Unknown"); std::cout << "' code: " << cur->vendor_id << "/" << cur->product_id << " (" << std::hex << cur->vendor_id << "/" << cur->product_id << std::dec << ")"; - std::cout << " serial number: "; + std::cout << " serial number: '"; std::wcout << ((cur->serial_number != nullptr) ? cur->serial_number : L"Unknown"); - std::cout << " usage page: " << cur->usage_page << " usage: " << cur->usage << std::endl; + std::cout << "' usage page: " << cur->usage_page << " usage: " << cur->usage << " interface number: " << cur->interface_number << std::endl; cur = cur->next; } #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + // When using 3Dconnexion universal receiver, multiple devices are detected sharing the same vendor_id and product_id. + // To choose from them the right one we use: + // On Windows and Mac: usage_page == 1 and usage == 8 + // On Linux: interface_number == 1, as usage_page and usage are not defined, see hidapi.h + // When only a single device is detected, as for wired connections, vendor_id and product_id are enough + + // First we count all the valid devices from the enumerated list, + hid_device_info* current = devices; + typedef std::pair DeviceIds; + typedef std::vector DeviceDataList; + typedef std::map DetectedDevices; + DetectedDevices detected_devices; while (current != nullptr) { + unsigned short vendor_id = 0; + unsigned short product_id = 0; + for (size_t i = 0; i < _3DCONNEXION_VENDORS.size(); ++i) { if (_3DCONNEXION_VENDORS[i] == current->vendor_id) @@ -370,28 +389,63 @@ bool Mouse3DController::connect_device() { for (size_t i = 0; i < _3DCONNEXION_DEVICES.size(); ++i) { - if ((_3DCONNEXION_DEVICES[i] == current->product_id) && (current->usage_page == 1) && (current->usage == 8)) + if (_3DCONNEXION_DEVICES[i] == current->product_id) { product_id = current->product_id; - path = current->path; - break; + DeviceIds detected_device(vendor_id, product_id); + DetectedDevices::iterator it = detected_devices.find(detected_device); + if (it == detected_devices.end()) + it = detected_devices.insert(DetectedDevices::value_type(detected_device, DeviceDataList())).first; + + it->second.push_back({ current->path, current->interface_number, current->usage_page, current->usage }); } } - - if (product_id == 0) - vendor_id = 0; } - if (vendor_id != 0) - break; - current = current->next; } // Free enumerated devices hid_free_enumeration(devices); - if (vendor_id == 0) + if (detected_devices.empty()) + return false; + + std::string path = ""; + unsigned short vendor_id = 0; + unsigned short product_id = 0; + + // Then we'll decide the choosing logic to apply in dependence of the device count and operating system + + for (const DetectedDevices::value_type& device : detected_devices) + { + if (device.second.size() == 1) + { + path = device.second.front().path; + vendor_id = device.first.first; + product_id = device.first.second; + break; + } + else + { + for (const DeviceData& data : device.second) + { +#if defined(__linux) + if (data.interface_number == 1) +#else + if ((data.usage_page == 1) && (data.usage == 8)) +#endif // __linux + { + path = data.path; + vendor_id = device.first.first; + product_id = device.first.second; + break; + } + } + } + } + + if (path.empty()) return false; // Open the 3Dconnexion device using the device path From d4f190a0c16292aefa8ecae18c0fc6a658c57adf Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 21 Oct 2019 15:20:36 +0200 Subject: [PATCH 33/67] Fixed build on Linux and Mac --- src/slic3r/GUI/Mouse3DController.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index fae62ca7e..78c0b6227 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -335,10 +335,17 @@ bool Mouse3DController::connect_device() // Searches for 1st connected 3Dconnexion device struct DeviceData { - std::string path{ "" }; - int interface_number{ 0 }; - unsigned short usage_page{ 0 }; - unsigned short usage{ 0 }; + std::string path; + int interface_number; + unsigned short usage_page; + unsigned short usage; + + DeviceData() + : path(""), interface_number(0), usage_page(0), usage(0) + {} + DeviceData(const std::string& path, int interface_number, unsigned short usage_page, unsigned short usage) + : path(path), interface_number(interface_number), usage_page(usage_page), usage(usage) + {} }; #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT @@ -397,7 +404,7 @@ bool Mouse3DController::connect_device() if (it == detected_devices.end()) it = detected_devices.insert(DetectedDevices::value_type(detected_device, DeviceDataList())).first; - it->second.push_back({ current->path, current->interface_number, current->usage_page, current->usage }); + it->second.emplace_back(current->path, current->interface_number, current->usage_page, current->usage); } } } From 55a4b99e44ea544612125ef3fb475a73579514f1 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 7 Nov 2019 11:48:59 +0100 Subject: [PATCH 34/67] Fixed bug into Camera::set_target() --- src/slic3r/GUI/Camera.cpp | 14 ++++++++------ src/slic3r/GUI/GLCanvas3D.cpp | 4 ++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/Camera.cpp b/src/slic3r/GUI/Camera.cpp index 59f0a6c47..6ccb5af49 100644 --- a/src/slic3r/GUI/Camera.cpp +++ b/src/slic3r/GUI/Camera.cpp @@ -91,14 +91,16 @@ void Camera::select_next_type() void Camera::set_target(const Vec3d& target) { - // We may let these factors be customizable - static const double ScaleFactor = 1.1; BoundingBoxf3 test_box = m_scene_box; + test_box.translate(-m_scene_box.center()); + // We may let this factor be customizable + static const double ScaleFactor = 1.5; test_box.scale(ScaleFactor); - m_target = target; - m_target(0) = clamp(test_box.min(0), test_box.max(0), m_target(0)); - m_target(1) = clamp(test_box.min(1), test_box.max(1), m_target(1)); - m_target(2) = clamp(test_box.min(2), test_box.max(2), m_target(2)); + test_box.translate(m_scene_box.center()); + + m_target(0) = clamp(test_box.min(0), test_box.max(0), target(0)); + m_target(1) = clamp(test_box.min(1), test_box.max(1), target(1)); + m_target(2) = clamp(test_box.min(2), test_box.max(2), target(2)); } void Camera::set_theta(float theta, bool apply_limit) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 7786f55f3..76990fb89 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1373,7 +1373,7 @@ void GLCanvas3D::set_model(Model* model) void GLCanvas3D::bed_shape_changed() { - m_camera.set_scene_box(scene_bounding_box()); + refresh_camera_scene_box(); m_camera.requires_zoom_to_bed = true; m_dirty = true; if (m_bed.is_prusa()) @@ -2116,7 +2116,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re post_event(Event(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, false)); } - m_camera.set_scene_box(scene_bounding_box()); + refresh_camera_scene_box(); if (m_selection.is_empty()) { From 6e4060569a833a61c257166d7906ce028d02a06d Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 7 Nov 2019 15:55:45 +0100 Subject: [PATCH 35/67] 1) Added new tech ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE 2) Imgui dialog replaces texture for reset button and tooltip when layer editing is active --- src/libslic3r/PrintObject.cpp | 9 ++- src/libslic3r/Technologies.hpp | 3 + src/slic3r/GUI/GLCanvas3D.cpp | 102 +++++++++++++++++++++++++++++++-- src/slic3r/GUI/GLCanvas3D.hpp | 27 ++++++++- src/slic3r/GUI/Plater.cpp | 3 + 5 files changed, 132 insertions(+), 12 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index c4ca46a8c..7819b8313 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1522,9 +1522,12 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c layer_height_profile.clear(); if (layer_height_profile.empty()) { - //layer_height_profile = layer_height_profile_adaptive(slicing_parameters, model_object.layer_config_ranges, model_object.volumes); - layer_height_profile = layer_height_profile_from_ranges(slicing_parameters, model_object.layer_config_ranges); - updated = true; +//#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE +// layer_height_profile = layer_height_profile_adaptive(slicing_parameters, model_object.layer_config_ranges, model_object.volumes); +//#else + layer_height_profile = layer_height_profile_from_ranges(slicing_parameters, model_object.layer_config_ranges); +//#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + updated = true; } return updated; } diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 5d0a7592c..f8db1b7b3 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -42,4 +42,7 @@ #define ENABLE_THUMBNAIL_GENERATOR_DEBUG (0 && ENABLE_THUMBNAIL_GENERATOR) #define ENABLE_THUMBNAIL_GENERATOR_PNG_TO_GCODE (1 && ENABLE_THUMBNAIL_GENERATOR) +// Enable adaptive layer height profile +#define ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE (1 && ENABLE_2_2_0_ALPHA1) + #endif // _technologies_h_ diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 99f61e51e..18ac3b10a 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -150,7 +150,9 @@ GLCanvas3D::LayersEditing::~LayersEditing() } const float GLCanvas3D::LayersEditing::THICKNESS_BAR_WIDTH = 70.0f; +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE const float GLCanvas3D::LayersEditing::THICKNESS_RESET_BUTTON_HEIGHT = 22.0f; +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE bool GLCanvas3D::LayersEditing::init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename) { @@ -217,13 +219,70 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const if (!m_enabled) return; +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + static const ImVec4 orange(0.757f, 0.404f, 0.216f, 1.0f); + + const Size& cnv_size = canvas.get_canvas_size(); + float canvas_w = (float)cnv_size.get_width(); + float canvas_h = (float)cnv_size.get_height(); + + + ImGuiWrapper& imgui = *wxGetApp().imgui(); + imgui.set_next_window_pos(canvas_w - THICKNESS_BAR_WIDTH, canvas_h, ImGuiCond_Always, 1.0f, 1.0f); + imgui.set_next_window_bg_alpha(0.5f); + + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + + imgui.begin(_(L("Layer height profile")), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse); + + ImGui::PushStyleColor(ImGuiCol_Text, orange); + imgui.text(_(L("Left mouse button:"))); + ImGui::PopStyleColor(); + ImGui::SameLine(); + imgui.text(_(L("Add detail"))); + + ImGui::PushStyleColor(ImGuiCol_Text, orange); + imgui.text(_(L("Right mouse button:"))); + ImGui::PopStyleColor(); + ImGui::SameLine(); + imgui.text(_(L("Remove detail"))); + + ImGui::PushStyleColor(ImGuiCol_Text, orange); + imgui.text(_(L("Shift + Left mouse button:"))); + ImGui::PopStyleColor(); + ImGui::SameLine(); + imgui.text(_(L("Reset to base"))); + + ImGui::PushStyleColor(ImGuiCol_Text, orange); + imgui.text(_(L("Shift + Right mouse button:"))); + ImGui::PopStyleColor(); + ImGui::SameLine(); + imgui.text(_(L("Smoothing"))); + + ImGui::PushStyleColor(ImGuiCol_Text, orange); + imgui.text(_(L("Mouse wheel:"))); + ImGui::PopStyleColor(); + ImGui::SameLine(); + imgui.text(_(L("Increase/decrease edit area"))); + + ImGui::Separator(); + if (imgui.button(_(L("Reset")))) + wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), SimpleEvent(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE)); + + imgui.end(); + + ImGui::PopStyleVar(); + + const Rect& bar_rect = get_bar_rect_viewport(canvas); +#else const Rect& bar_rect = get_bar_rect_viewport(canvas); const Rect& reset_rect = get_reset_rect_viewport(canvas); _render_tooltip_texture(canvas, bar_rect, reset_rect); _render_reset_texture(reset_rect); - _render_active_object_annotations(canvas, bar_rect); - _render_profile(bar_rect); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + render_active_object_annotations(canvas, bar_rect); + render_profile(bar_rect); } float GLCanvas3D::LayersEditing::get_cursor_z_relative(const GLCanvas3D& canvas) @@ -248,11 +307,13 @@ bool GLCanvas3D::LayersEditing::bar_rect_contains(const GLCanvas3D& canvas, floa return (rect.get_left() <= x) && (x <= rect.get_right()) && (rect.get_top() <= y) && (y <= rect.get_bottom()); } +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE bool GLCanvas3D::LayersEditing::reset_rect_contains(const GLCanvas3D& canvas, float x, float y) { const Rect& rect = get_reset_rect_screen(canvas); return (rect.get_left() <= x) && (x <= rect.get_right()) && (rect.get_top() <= y) && (y <= rect.get_bottom()); } +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE Rect GLCanvas3D::LayersEditing::get_bar_rect_screen(const GLCanvas3D& canvas) { @@ -260,9 +321,14 @@ Rect GLCanvas3D::LayersEditing::get_bar_rect_screen(const GLCanvas3D& canvas) float w = (float)cnv_size.get_width(); float h = (float)cnv_size.get_height(); +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + return Rect(w - thickness_bar_width(canvas), 0.0f, w, h); +#else return Rect(w - thickness_bar_width(canvas), 0.0f, w, h - reset_button_height(canvas)); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE } +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE Rect GLCanvas3D::LayersEditing::get_reset_rect_screen(const GLCanvas3D& canvas) { const Size& cnv_size = canvas.get_canvas_size(); @@ -271,6 +337,7 @@ Rect GLCanvas3D::LayersEditing::get_reset_rect_screen(const GLCanvas3D& canvas) return Rect(w - thickness_bar_width(canvas), h - reset_button_height(canvas), w, h); } +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE Rect GLCanvas3D::LayersEditing::get_bar_rect_viewport(const GLCanvas3D& canvas) { @@ -281,9 +348,14 @@ Rect GLCanvas3D::LayersEditing::get_bar_rect_viewport(const GLCanvas3D& canvas) float zoom = (float)canvas.get_camera().get_zoom(); float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + return Rect((half_w - thickness_bar_width(canvas)) * inv_zoom, half_h * inv_zoom, half_w * inv_zoom, -half_h * inv_zoom); +#else return Rect((half_w - thickness_bar_width(canvas)) * inv_zoom, half_h * inv_zoom, half_w * inv_zoom, (-half_h + reset_button_height(canvas)) * inv_zoom); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE } +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE Rect GLCanvas3D::LayersEditing::get_reset_rect_viewport(const GLCanvas3D& canvas) { const Size& cnv_size = canvas.get_canvas_size(); @@ -295,13 +367,14 @@ Rect GLCanvas3D::LayersEditing::get_reset_rect_viewport(const GLCanvas3D& canvas return Rect((half_w - thickness_bar_width(canvas)) * inv_zoom, (-half_h + reset_button_height(canvas)) * inv_zoom, half_w * inv_zoom, -half_h * inv_zoom); } +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE - -bool GLCanvas3D::LayersEditing::_is_initialized() const +bool GLCanvas3D::LayersEditing::is_initialized() const { return m_shader.is_initialized(); } +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE void GLCanvas3D::LayersEditing::_render_tooltip_texture(const GLCanvas3D& canvas, const Rect& bar_rect, const Rect& reset_rect) const { // TODO: do this with ImGui @@ -347,8 +420,9 @@ void GLCanvas3D::LayersEditing::_render_reset_texture(const Rect& reset_rect) co GLTexture::render_texture(m_reset_texture.get_id(), reset_rect.get_left(), reset_rect.get_right(), reset_rect.get_bottom(), reset_rect.get_top()); } +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE -void GLCanvas3D::LayersEditing::_render_active_object_annotations(const GLCanvas3D& canvas, const Rect& bar_rect) const +void GLCanvas3D::LayersEditing::render_active_object_annotations(const GLCanvas3D& canvas, const Rect& bar_rect) const { m_shader.start_using(); @@ -379,7 +453,7 @@ void GLCanvas3D::LayersEditing::_render_active_object_annotations(const GLCanvas m_shader.stop_using(); } -void GLCanvas3D::LayersEditing::_render_profile(const Rect& bar_rect) const +void GLCanvas3D::LayersEditing::render_profile(const Rect& bar_rect) const { //FIXME show some kind of legend. @@ -557,6 +631,7 @@ float GLCanvas3D::LayersEditing::thickness_bar_width(const GLCanvas3D &canvas) * THICKNESS_BAR_WIDTH; } +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE float GLCanvas3D::LayersEditing::reset_button_height(const GLCanvas3D &canvas) { return @@ -567,6 +642,7 @@ float GLCanvas3D::LayersEditing::reset_button_height(const GLCanvas3D &canvas) #endif * THICKNESS_RESET_BUTTON_HEIGHT; } +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE const Point GLCanvas3D::Mouse::Drag::Invalid_2D_Point(INT_MAX, INT_MAX); @@ -1118,6 +1194,9 @@ wxDEFINE_EVENT(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, wxKeyEvent); wxDEFINE_EVENT(EVT_GLCANVAS_EDIT_COLOR_CHANGE, wxKeyEvent); wxDEFINE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent); +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE +wxDEFINE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE #if ENABLE_THUMBNAIL_GENERATOR const double GLCanvas3D::DefaultCameraZoomToBoxMarginFactor = 1.25; @@ -1420,6 +1499,15 @@ bool GLCanvas3D::is_layers_editing_allowed() const return m_layers_editing.is_allowed(); } +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE +void GLCanvas3D::reset_layer_height_profile() +{ + m_layers_editing.reset_layer_height_profile(*this); + m_layers_editing.state = LayersEditing::Completed; + m_dirty = true; +} +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + bool GLCanvas3D::is_reload_delayed() const { return m_reload_delayed; @@ -2814,6 +2902,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_layers_editing.state = LayersEditing::Editing; _perform_layer_editing_action(&evt); } +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE else if ((layer_editing_object_idx != -1) && m_layers_editing.reset_rect_contains(*this, pos(0), pos(1))) { if (evt.LeftDown()) @@ -2826,6 +2915,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_dirty = true; } } +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE else if (evt.LeftDown() && (evt.ShiftDown() || evt.AltDown()) && m_picking_enabled) { if (m_gizmos.get_current_type() != GLGizmosManager::SlaSupports) diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 8c2e6e9a5..da55210a8 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -104,6 +104,9 @@ wxDECLARE_EVENT(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, wxKeyEvent); wxDECLARE_EVENT(EVT_GLCANVAS_EDIT_COLOR_CHANGE, wxKeyEvent); wxDECLARE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent); +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE +wxDECLARE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE class GLCanvas3D { @@ -153,13 +156,17 @@ private: private: static const float THICKNESS_BAR_WIDTH; +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE static const float THICKNESS_RESET_BUTTON_HEIGHT; +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE bool m_enabled; Shader m_shader; unsigned int m_z_texture_id; +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE mutable GLTexture m_tooltip_texture; mutable GLTexture m_reset_texture; +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE // Not owned by LayersEditing. const DynamicPrintConfig *m_config; // ModelObject for the currently selected object (Model::objects[last_object_id]). @@ -220,25 +227,35 @@ private: static float get_cursor_z_relative(const GLCanvas3D& canvas); static bool bar_rect_contains(const GLCanvas3D& canvas, float x, float y); +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE static bool reset_rect_contains(const GLCanvas3D& canvas, float x, float y); +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE static Rect get_bar_rect_screen(const GLCanvas3D& canvas); +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE static Rect get_reset_rect_screen(const GLCanvas3D& canvas); +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE static Rect get_bar_rect_viewport(const GLCanvas3D& canvas); +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE static Rect get_reset_rect_viewport(const GLCanvas3D& canvas); +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE float object_max_z() const { return m_object_max_z; } private: - bool _is_initialized() const; + bool is_initialized() const; void generate_layer_height_texture(); +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE void _render_tooltip_texture(const GLCanvas3D& canvas, const Rect& bar_rect, const Rect& reset_rect) const; void _render_reset_texture(const Rect& reset_rect) const; - void _render_active_object_annotations(const GLCanvas3D& canvas, const Rect& bar_rect) const; - void _render_profile(const Rect& bar_rect) const; +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + void render_active_object_annotations(const GLCanvas3D& canvas, const Rect& bar_rect) const; + void render_profile(const Rect& bar_rect) const; void update_slicing_parameters(); static float thickness_bar_width(const GLCanvas3D &canvas); +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE static float reset_button_height(const GLCanvas3D &canvas); +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE }; struct Mouse @@ -500,6 +517,10 @@ public: bool is_layers_editing_enabled() const; bool is_layers_editing_allowed() const; +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + void reset_layer_height_profile(); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + bool is_reload_delayed() const; void enable_layers_editing(bool enable); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index cdd35b720..44de82861 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2070,6 +2070,9 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) view3D_canvas->Bind(EVT_GLCANVAS_RESETGIZMOS, [this](SimpleEvent&) { reset_all_gizmos(); }); view3D_canvas->Bind(EVT_GLCANVAS_UNDO, [this](SimpleEvent&) { this->undo(); }); view3D_canvas->Bind(EVT_GLCANVAS_REDO, [this](SimpleEvent&) { this->redo(); }); +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + view3D_canvas->Bind(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, [this](SimpleEvent&) { this->view3D->get_canvas3d()->reset_layer_height_profile(); }); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE // 3DScene/Toolbar: view3D_canvas->Bind(EVT_GLTOOLBAR_ADD, &priv::on_action_add, this); From 68d6a458150ea194a9202853b106bc8d4b286ca6 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 8 Nov 2019 08:47:57 +0100 Subject: [PATCH 36/67] 3D connexion devides -> Added handling of 3 bytes long button packets --- src/slic3r/GUI/Mouse3DController.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 78c0b6227..96ef4f664 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -569,6 +569,9 @@ void Mouse3DController::collect_input() updated = handle_packet(packet); else if (res == 13) updated = handle_wireless_packet(packet); + else if ((res == 3) && (packet[0] == 3)) + // On Mac button packets can be 3 bytes long + updated = handle_packet(packet); #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT else if (res > 0) std::cout << "Got unknown data packet of length: " << res << ", code:" << (int)packet[0] << std::endl; @@ -599,7 +602,8 @@ bool Mouse3DController::handle_packet(const DataPacket& packet) } case 3: // Button { - if (handle_packet_button(packet, 6)) + unsigned int size = packet.size(); + if (handle_packet_button(packet, packet.size() - 1)) return true; break; From 4320b8f7111c482c7ba4dae35102d6b31629202c Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 8 Nov 2019 09:18:59 +0100 Subject: [PATCH 37/67] 3Dconnexion devices -> Fix into Mouse3DController::handle_packet_button --- src/slic3r/GUI/Mouse3DController.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 96ef4f664..352a5bf69 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -602,7 +602,6 @@ bool Mouse3DController::handle_packet(const DataPacket& packet) } case 3: // Button { - unsigned int size = packet.size(); if (handle_packet_button(packet, packet.size() - 1)) return true; @@ -708,7 +707,12 @@ bool Mouse3DController::handle_packet_rotation(const DataPacket& packet, unsigne bool Mouse3DController::handle_packet_button(const DataPacket& packet, unsigned int packet_size) { - unsigned int data = packet[1] | packet[2] << 8 | packet[3] << 16 | packet[4] << 24; + unsigned int data = 0; + for (unsigned int i = 1; i < packet_size; ++i) + { + data |= packet[i] << 8 * (i - 1); + } + const std::bitset<32> data_bits{ data }; for (size_t i = 0; i < data_bits.size(); ++i) { From b10d128ffd5414153908b91e46b3344cf3c17348 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 8 Nov 2019 11:51:56 +0100 Subject: [PATCH 38/67] 3Dconnexion devices -> Modified logic to select device on Linux --- src/slic3r/GUI/Mouse3DController.cpp | 102 +++++++++++++++++++++------ 1 file changed, 79 insertions(+), 23 deletions(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 352a5bf69..7a3443df0 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -336,39 +336,53 @@ bool Mouse3DController::connect_device() struct DeviceData { std::string path; - int interface_number; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +// int interface_number; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ unsigned short usage_page; unsigned short usage; DeviceData() - : path(""), interface_number(0), usage_page(0), usage(0) +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + : path(""), usage_page(0), usage(0) +// : path(""), interface_number(0), usage_page(0), usage(0) +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ {} - DeviceData(const std::string& path, int interface_number, unsigned short usage_page, unsigned short usage) - : path(path), interface_number(interface_number), usage_page(usage_page), usage(usage) +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + DeviceData(const std::string& path, unsigned short usage_page, unsigned short usage) + : path(path), usage_page(usage_page), usage(usage) +// DeviceData(const std::string& path, int interface_number, unsigned short usage_page, unsigned short usage) +// : path(path), interface_number(interface_number), usage_page(usage_page), usage(usage) +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ {} }; -#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT - hid_device_info* cur = devices; - while (cur != nullptr) - { - std::cout << "Detected device '"; - std::wcout << ((cur->manufacturer_string != nullptr) ? cur->manufacturer_string : L"Unknown"); - std::cout << "::"; - std::wcout << ((cur->product_string != nullptr) ? cur->product_string : L"Unknown"); - std::cout << "' code: " << cur->vendor_id << "/" << cur->product_id << " (" << std::hex << cur->vendor_id << "/" << cur->product_id << std::dec << ")"; - std::cout << " serial number: '"; - std::wcout << ((cur->serial_number != nullptr) ? cur->serial_number : L"Unknown"); - std::cout << "' usage page: " << cur->usage_page << " usage: " << cur->usage << " interface number: " << cur->interface_number << std::endl; - - cur = cur->next; - } -#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +//#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +// hid_device_info* cur = devices; +// while (cur != nullptr) +// { +// std::cout << "Detected device '"; +// std::wcout << ((cur->manufacturer_string != nullptr) ? cur->manufacturer_string : L"Unknown"); +// std::cout << "::"; +// std::wcout << ((cur->product_string != nullptr) ? cur->product_string : L"Unknown"); +// std::cout << "' code: " << cur->vendor_id << "/" << cur->product_id << " (" << std::hex << cur->vendor_id << "/" << cur->product_id << std::dec << ")"; +// std::cout << " serial number: '"; +// std::wcout << ((cur->serial_number != nullptr) ? cur->serial_number : L"Unknown"); +// std::cout << "' usage page: " << cur->usage_page << " usage: " << cur->usage << " interface number: " << cur->interface_number << std::endl; +// +// cur = cur->next; +// } +//#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // When using 3Dconnexion universal receiver, multiple devices are detected sharing the same vendor_id and product_id. // To choose from them the right one we use: // On Windows and Mac: usage_page == 1 and usage == 8 - // On Linux: interface_number == 1, as usage_page and usage are not defined, see hidapi.h +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + // On Linux: as usage_page and usage are not defined (see hidapi.h) we try all detected devices until one is succesfully open +// // On Linux: interface_number == 1, as usage_page and usage are not defined, see hidapi.h +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // When only a single device is detected, as for wired connections, vendor_id and product_id are enough // First we count all the valid devices from the enumerated list, @@ -378,6 +392,11 @@ bool Mouse3DController::connect_device() typedef std::vector DeviceDataList; typedef std::map DetectedDevices; DetectedDevices detected_devices; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + std::cout << std::endl << "Detected devices:" << std::endl; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ while (current != nullptr) { unsigned short vendor_id = 0; @@ -404,7 +423,22 @@ bool Mouse3DController::connect_device() if (it == detected_devices.end()) it = detected_devices.insert(DetectedDevices::value_type(detected_device, DeviceDataList())).first; - it->second.emplace_back(current->path, current->interface_number, current->usage_page, current->usage); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + it->second.emplace_back(current->path, current->usage_page, current->usage); +// it->second.emplace_back(current->path, current->interface_number, current->usage_page, current->usage); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + std::wcout << "\"" << ((current->manufacturer_string != nullptr) ? current->manufacturer_string : L"Unknown"); + std::cout << "/"; + std::wcout << ((current->product_string != nullptr) ? current->product_string : L"Unknown"); + std::cout << "\" code: " << current->vendor_id << "/" << current->product_id << " (" << std::hex << current->vendor_id << "/" << current->product_id << std::dec << ")"; + std::cout << " serial number: '"; + std::wcout << ((current->serial_number != nullptr) ? current->serial_number : L"Unknown"); + std::cout << "' usage page: " << current->usage_page << " usage: " << current->usage << std::endl; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } } } @@ -435,10 +469,25 @@ bool Mouse3DController::connect_device() } else { +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + bool found = false; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ for (const DeviceData& data : device.second) { #if defined(__linux) - if (data.interface_number == 1) +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + hid_device* test_device = hid_open_path(data.path.c_str()); + if (test_device != nullptr) + { + path = data.path; + vendor_id = device.first.first; + product_id = device.first.second; + found = true; + hid_close(test_device); + break; + } +// if (data.interface_number == 1) +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #else if ((data.usage_page == 1) && (data.usage == 8)) #endif // __linux @@ -446,9 +495,16 @@ bool Mouse3DController::connect_device() path = data.path; vendor_id = device.first.first; product_id = device.first.second; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + found = true; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ break; } } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + if (found) + break; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } } From a87d83aeb18b5f130caafcc31ecb556bc2f8f735 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 8 Nov 2019 13:31:34 +0100 Subject: [PATCH 39/67] 3Dconnexion devices -> Another change in logic to select device --- src/slic3r/GUI/Mouse3DController.cpp | 30 ++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 7a3443df0..c1d85044c 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -462,7 +462,9 @@ bool Mouse3DController::connect_device() { if (device.second.size() == 1) { - path = device.second.front().path; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +// path = device.second.front().path; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ vendor_id = device.first.first; product_id = device.first.second; break; @@ -484,6 +486,12 @@ bool Mouse3DController::connect_device() product_id = device.first.second; found = true; hid_close(test_device); +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + std::cout << std::endl << "PASSED TEST device:" << std::endl; + std::cout << "Manufacturer id.....: " << vendor_id << " (" << std::hex << vendor_id << std::dec << ")" << std::endl; + std::cout << "Product id..........: " << product_id << " (" << std::hex << product_id << std::dec << ")" << std::endl; + std::cout << "Path................: '" << path << "'" << std::endl; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT break; } // if (data.interface_number == 1) @@ -508,11 +516,25 @@ bool Mouse3DController::connect_device() } } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (path.empty()) - return false; + { + if ((vendor_id != 0) && (product_id != 0)) + // Open the 3Dconnexion device using vendor_id and product_id + m_device = hid_open(vendor_id, product_id, nullptr); + else + return false; + } + else + // Open the 3Dconnexion device using the device path + m_device = hid_open_path(path.c_str()); - // Open the 3Dconnexion device using the device path - m_device = hid_open_path(path.c_str()); +// if (path.empty()) +// return false; +// +// // Open the 3Dconnexion device using the device path +// m_device = hid_open_path(path.c_str()); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (m_device != nullptr) { From 08861250bf579b1f3164027c1d4541c2b5299548 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 8 Nov 2019 14:43:15 +0100 Subject: [PATCH 40/67] 3Dconnexion devices -> Added extra debug output --- src/slic3r/GUI/Mouse3DController.cpp | 41 +++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index c1d85044c..bf6dca20a 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -394,6 +394,7 @@ bool Mouse3DController::connect_device() DetectedDevices detected_devices; //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + std::cout << std::endl << "======================================================================================================================================" << std::endl; std::cout << std::endl << "Detected devices:" << std::endl; #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@ -473,10 +474,19 @@ bool Mouse3DController::connect_device() { //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ bool found = false; +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + std::cout << std::endl; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ for (const DeviceData& data : device.second) { -#if defined(__linux) +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + std::cout << "Test device: " << std::hex << device.first.first << std::dec << "/" << std::hex << device.first.second << std::dec << " \"" << data.path << "\""; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +#ifdef __linux__ +//#if defined(__linux) +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ hid_device* test_device = hid_open_path(data.path.c_str()); if (test_device != nullptr) @@ -485,29 +495,36 @@ bool Mouse3DController::connect_device() vendor_id = device.first.first; product_id = device.first.second; found = true; - hid_close(test_device); #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT - std::cout << std::endl << "PASSED TEST device:" << std::endl; - std::cout << "Manufacturer id.....: " << vendor_id << " (" << std::hex << vendor_id << std::dec << ")" << std::endl; - std::cout << "Product id..........: " << product_id << " (" << std::hex << product_id << std::dec << ")" << std::endl; - std::cout << "Path................: '" << path << "'" << std::endl; + std::cout << "-> PASSED" << std::endl; #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + hid_close(test_device); break; } // if (data.interface_number == 1) //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #else if ((data.usage_page == 1) && (data.usage == 8)) -#endif // __linux +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // __linux__ +//#endif // __linux +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ { path = data.path; vendor_id = device.first.first; product_id = device.first.second; //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ found = true; +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + std::cout << "-> PASSED" << std::endl; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ break; } +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + else + std::cout << "-> NOT PASSED" << std::endl; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT } //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (found) @@ -520,14 +537,24 @@ bool Mouse3DController::connect_device() if (path.empty()) { if ((vendor_id != 0) && (product_id != 0)) + { // Open the 3Dconnexion device using vendor_id and product_id +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + std::cout << std::endl << "Opening device: " << std::hex << vendor_id << std::dec << "/" << std::hex << product_id << std::dec << " using hid_open()" << std::endl; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT m_device = hid_open(vendor_id, product_id, nullptr); + } else return false; } else + { // Open the 3Dconnexion device using the device path +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + std::cout << std::endl << "Opening device: " << std::hex << vendor_id << std::dec << "/" << std::hex << product_id << std::dec << "\"" << path << "\" using hid_open_path()" << std::endl; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT m_device = hid_open_path(path.c_str()); + } // if (path.empty()) // return false; From 932b5be19680c9a30cc5076228b3aae3ff711820 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 8 Nov 2019 15:54:44 +0100 Subject: [PATCH 41/67] Fixed build on Linux --- src/slic3r/GUI/Mouse3DController.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index bf6dca20a..36da0536d 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -506,7 +506,6 @@ bool Mouse3DController::connect_device() #else if ((data.usage_page == 1) && (data.usage == 8)) //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#endif // __linux__ //#endif // __linux //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ { @@ -521,6 +520,9 @@ bool Mouse3DController::connect_device() //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ break; } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // __linux__ +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT else std::cout << "-> NOT PASSED" << std::endl; From dbc0996be10dfa5cf36858334d9b8a01b2fb16a9 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 11 Nov 2019 13:04:02 +0100 Subject: [PATCH 42/67] 3Dconnexion devices -> Disabled handling of device buttons / Added camera target to debug dialog / Added extra debug output --- src/slic3r/GUI/Mouse3DController.cpp | 123 +++++++++++---------------- src/slic3r/GUI/Mouse3DController.hpp | 2 + 2 files changed, 51 insertions(+), 74 deletions(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 36da0536d..516c7dc9e 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -58,7 +58,8 @@ const float Mouse3DController::State::MaxRotationDeadzone = (float)Mouse3DContro const float Mouse3DController::State::DefaultRotationDeadzone = 0.5f * Mouse3DController::State::MaxRotationDeadzone; Mouse3DController::State::State() - : m_translation_params(DefaultTranslationScale, DefaultTranslationDeadzone) + : m_buttons_enabled(false) + , m_translation_params(DefaultTranslationScale, DefaultTranslationDeadzone) , m_rotation_params(DefaultRotationScale, DefaultRotationDeadzone) , m_mouse_wheel_counter(0) #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT @@ -97,6 +98,9 @@ void Mouse3DController::State::append_rotation(const Vec3f& rotation) void Mouse3DController::State::append_button(unsigned int id) { + if (!m_buttons_enabled) + return; + m_buttons.push(id); #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT m_buttons_queue_max_size = std::max(m_buttons_queue_max_size, m_buttons.size()); @@ -159,7 +163,7 @@ bool Mouse3DController::State::apply(Camera& camera) ret = true; } - if (has_button()) + if (m_buttons_enabled && has_button()) { unsigned int button = m_buttons.front(); switch (button) @@ -312,6 +316,13 @@ void Mouse3DController::render_settings_dialog(unsigned int canvas_width, unsign if (queue_size > 0) m_state.set_queues_max_size(queue_size); } + + ImGui::Separator(); + ImGui::PushStyleColor(ImGuiCol_Text, color); + imgui.text("Camera:"); + ImGui::PopStyleColor(); + Vec3f target = wxGetApp().plater()->get_camera().get_target().cast(); + ImGui::InputFloat3("Target", target.data(), "%.3f", ImGuiInputTextFlags_ReadOnly); #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT imgui.end(); @@ -336,53 +347,42 @@ bool Mouse3DController::connect_device() struct DeviceData { std::string path; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -// int interface_number; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ unsigned short usage_page; unsigned short usage; DeviceData() -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ : path(""), usage_page(0), usage(0) -// : path(""), interface_number(0), usage_page(0), usage(0) -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ {} -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ DeviceData(const std::string& path, unsigned short usage_page, unsigned short usage) : path(path), usage_page(usage_page), usage(usage) -// DeviceData(const std::string& path, int interface_number, unsigned short usage_page, unsigned short usage) -// : path(path), interface_number(interface_number), usage_page(usage_page), usage(usage) -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ {} + + bool has_valid_usage() const { return (usage_page == 1) && (usage == 8); } }; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -//#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT -// hid_device_info* cur = devices; -// while (cur != nullptr) -// { -// std::cout << "Detected device '"; -// std::wcout << ((cur->manufacturer_string != nullptr) ? cur->manufacturer_string : L"Unknown"); -// std::cout << "::"; -// std::wcout << ((cur->product_string != nullptr) ? cur->product_string : L"Unknown"); -// std::cout << "' code: " << cur->vendor_id << "/" << cur->product_id << " (" << std::hex << cur->vendor_id << "/" << cur->product_id << std::dec << ")"; -// std::cout << " serial number: '"; -// std::wcout << ((cur->serial_number != nullptr) ? cur->serial_number : L"Unknown"); -// std::cout << "' usage page: " << cur->usage_page << " usage: " << cur->usage << " interface number: " << cur->interface_number << std::endl; -// -// cur = cur->next; -// } -//#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + hid_device_info* cur = devices; + std::cout << std::endl << "======================================================================================================================================" << std::endl; + std::cout << "Detected devices:" << std::endl; + while (cur != nullptr) + { + std::cout << "\""; + std::wcout << ((cur->manufacturer_string != nullptr) ? cur->manufacturer_string : L"Unknown"); + std::cout << "/"; + std::wcout << ((cur->product_string != nullptr) ? cur->product_string : L"Unknown"); + std::cout << "\" code: " << cur->vendor_id << "/" << cur->product_id << " (" << std::hex << cur->vendor_id << "/" << cur->product_id << std::dec << ")"; + std::cout << " serial number: '"; + std::wcout << ((cur->serial_number != nullptr) ? cur->serial_number : L"Unknown"); + std::cout << "' usage page: " << cur->usage_page << " usage: " << cur->usage << " interface number: " << cur->interface_number << std::endl; + + cur = cur->next; + } +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT // When using 3Dconnexion universal receiver, multiple devices are detected sharing the same vendor_id and product_id. // To choose from them the right one we use: // On Windows and Mac: usage_page == 1 and usage == 8 -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // On Linux: as usage_page and usage are not defined (see hidapi.h) we try all detected devices until one is succesfully open -// // On Linux: interface_number == 1, as usage_page and usage are not defined, see hidapi.h -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // When only a single device is detected, as for wired connections, vendor_id and product_id are enough // First we count all the valid devices from the enumerated list, @@ -392,12 +392,9 @@ bool Mouse3DController::connect_device() typedef std::vector DeviceDataList; typedef std::map DetectedDevices; DetectedDevices detected_devices; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT - std::cout << std::endl << "======================================================================================================================================" << std::endl; - std::cout << std::endl << "Detected devices:" << std::endl; + std::cout << std::endl << "Detected 3D connexion devices:" << std::endl; #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ while (current != nullptr) { unsigned short vendor_id = 0; @@ -424,12 +421,8 @@ bool Mouse3DController::connect_device() if (it == detected_devices.end()) it = detected_devices.insert(DetectedDevices::value_type(detected_device, DeviceDataList())).first; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ it->second.emplace_back(current->path, current->usage_page, current->usage); -// it->second.emplace_back(current->path, current->interface_number, current->usage_page, current->usage); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT std::wcout << "\"" << ((current->manufacturer_string != nullptr) ? current->manufacturer_string : L"Unknown"); std::cout << "/"; @@ -439,7 +432,6 @@ bool Mouse3DController::connect_device() std::wcout << ((current->serial_number != nullptr) ? current->serial_number : L"Unknown"); std::cout << "' usage page: " << current->usage_page << " usage: " << current->usage << std::endl; #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } } } @@ -463,31 +455,32 @@ bool Mouse3DController::connect_device() { if (device.second.size() == 1) { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -// path = device.second.front().path; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - vendor_id = device.first.first; - product_id = device.first.second; - break; +#ifdef __linux__ + hid_device* test_device = hid_open(device.first.first, device.first.second); + if (test_device != nullptr) + { + hid_close(test_device); +#else + if (device.second.front().has_valid_usage()) + { +#endif // __linux__ + vendor_id = device.first.first; + product_id = device.first.second; + break; + } } else { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ bool found = false; #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT std::cout << std::endl; #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ for (const DeviceData& data : device.second) { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT std::cout << "Test device: " << std::hex << device.first.first << std::dec << "/" << std::hex << device.first.second << std::dec << " \"" << data.path << "\""; #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT #ifdef __linux__ -//#if defined(__linux) -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ hid_device* test_device = hid_open_path(data.path.c_str()); if (test_device != nullptr) { @@ -501,41 +494,30 @@ bool Mouse3DController::connect_device() hid_close(test_device); break; } -// if (data.interface_number == 1) -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #else - if ((data.usage_page == 1) && (data.usage == 8)) -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -//#endif // __linux -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + if (data.has_valid_usage()) { path = data.path; vendor_id = device.first.first; product_id = device.first.second; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ found = true; #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT std::cout << "-> PASSED" << std::endl; #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ break; } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // __linux__ -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT else std::cout << "-> NOT PASSED" << std::endl; #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + if (found) break; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (path.empty()) { if ((vendor_id != 0) && (product_id != 0)) @@ -558,13 +540,6 @@ bool Mouse3DController::connect_device() m_device = hid_open_path(path.c_str()); } -// if (path.empty()) -// return false; -// -// // Open the 3Dconnexion device using the device path -// m_device = hid_open_path(path.c_str()); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - if (m_device != nullptr) { std::vector manufacturer(1024, 0); diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index 9812cb742..45616091f 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -56,6 +56,8 @@ class Mouse3DController InputQueue m_rotation; std::queue m_buttons; + bool m_buttons_enabled; + CustomParameters m_translation_params; CustomParameters m_rotation_params; From db5180919163743ecec3593b20b4fceb19414b1a Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 11 Nov 2019 13:41:50 +0100 Subject: [PATCH 43/67] Fixed build on Linux --- src/slic3r/GUI/Mouse3DController.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 516c7dc9e..44bc0fd34 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -456,7 +456,7 @@ bool Mouse3DController::connect_device() if (device.second.size() == 1) { #ifdef __linux__ - hid_device* test_device = hid_open(device.first.first, device.first.second); + hid_device* test_device = hid_open(device.first.first, device.first.second, nullptr); if (test_device != nullptr) { hid_close(test_device); From b77ba32bb2171ae55254d5ac50f08d7f11911192 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 12 Nov 2019 14:18:43 +0100 Subject: [PATCH 44/67] Adaptive layer height profile -> Added Adaptive button to imgui dialog --- src/libslic3r/PrintObject.cpp | 5 +---- src/libslic3r/Slicing.cpp | 6 +++++- src/slic3r/GUI/GLCanvas3D.cpp | 22 ++++++++++++++++++++++ src/slic3r/GUI/GLCanvas3D.hpp | 5 +++++ src/slic3r/GUI/Plater.cpp | 1 + 5 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 7819b8313..bf97baaf6 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1522,11 +1522,8 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c layer_height_profile.clear(); if (layer_height_profile.empty()) { -//#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE -// layer_height_profile = layer_height_profile_adaptive(slicing_parameters, model_object.layer_config_ranges, model_object.volumes); -//#else + //layer_height_profile = layer_height_profile_adaptive(slicing_parameters, model_object.layer_config_ranges, model_object.volumes); layer_height_profile = layer_height_profile_from_ranges(slicing_parameters, model_object.layer_config_ranges); -//#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE updated = true; } return updated; diff --git a/src/libslic3r/Slicing.cpp b/src/libslic3r/Slicing.cpp index c62736ffe..1c1a1159f 100644 --- a/src/libslic3r/Slicing.cpp +++ b/src/libslic3r/Slicing.cpp @@ -609,7 +609,11 @@ int generate_layer_height_texture( const Vec3crd &color1 = palette_raw[idx1]; const Vec3crd &color2 = palette_raw[idx2]; coordf_t z = cell_to_z * coordf_t(cell); - assert(z >= lo && z <= hi); +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + assert((lo - EPSILON <= z) && (z <= hi + EPSILON)); +#else + assert(z >= lo && z <= hi); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE // Intensity profile to visualize the layers. coordf_t intensity = cos(M_PI * 0.7 * (mid - z) / h); // Color mapping from layer height to RGB. diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index fb01726b0..7b1dfb89a 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -269,6 +269,10 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const if (imgui.button(_(L("Reset")))) wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), SimpleEvent(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE)); + ImGui::SameLine(); + if (imgui.button(_(L("Adaptive")))) + wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), SimpleEvent(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE)); + imgui.end(); ImGui::PopStyleVar(); @@ -570,6 +574,16 @@ void GLCanvas3D::LayersEditing::reset_layer_height_profile(GLCanvas3D& canvas) canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); } +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE +void GLCanvas3D::LayersEditing::adaptive_layer_height_profile(GLCanvas3D& canvas) +{ + const_cast(m_model_object)->layer_height_profile.clear(); + m_layer_height_profile = layer_height_profile_adaptive(*m_slicing_parameters, m_model_object->layer_config_ranges, m_model_object->volumes); + m_layers_texture.valid = false; + canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); +} +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + void GLCanvas3D::LayersEditing::generate_layer_height_texture() { this->update_slicing_parameters(); @@ -1196,6 +1210,7 @@ wxDEFINE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent); #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE wxDEFINE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent); +wxDEFINE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, SimpleEvent); #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE #if ENABLE_THUMBNAIL_GENERATOR @@ -1506,6 +1521,13 @@ void GLCanvas3D::reset_layer_height_profile() m_layers_editing.state = LayersEditing::Completed; m_dirty = true; } + +void GLCanvas3D::adaptive_layer_height_profile() +{ + m_layers_editing.adaptive_layer_height_profile(*this); + m_layers_editing.state = LayersEditing::Completed; + m_dirty = true; +} #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE bool GLCanvas3D::is_reload_delayed() const diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index da55210a8..d17174628 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -106,6 +106,7 @@ wxDECLARE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent); #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE wxDECLARE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent); +wxDECLARE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, SimpleEvent); #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE class GLCanvas3D @@ -224,6 +225,9 @@ private: void adjust_layer_height_profile(); void accept_changes(GLCanvas3D& canvas); void reset_layer_height_profile(GLCanvas3D& canvas); +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + void adaptive_layer_height_profile(GLCanvas3D& canvas); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE static float get_cursor_z_relative(const GLCanvas3D& canvas); static bool bar_rect_contains(const GLCanvas3D& canvas, float x, float y); @@ -519,6 +523,7 @@ public: #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE void reset_layer_height_profile(); + void adaptive_layer_height_profile(); #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE bool is_reload_delayed() const; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 084d821ba..ac9c5cc4b 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2091,6 +2091,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) view3D_canvas->Bind(EVT_GLCANVAS_REDO, [this](SimpleEvent&) { this->redo(); }); #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE view3D_canvas->Bind(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, [this](SimpleEvent&) { this->view3D->get_canvas3d()->reset_layer_height_profile(); }); + view3D_canvas->Bind(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, [this](SimpleEvent&) { this->view3D->get_canvas3d()->adaptive_layer_height_profile(); }); #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE // 3DScene/Toolbar: From 0001ce3dab749d130fc2ead976aed3ba753d9b7f Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 13 Nov 2019 13:53:02 +0100 Subject: [PATCH 45/67] ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE -> Refactoring of SlicingAdaptive to account for volumes' transformation --- src/libslic3r/Slicing.cpp | 24 ++++++++--- src/libslic3r/Slicing.hpp | 11 +++++- src/libslic3r/SlicingAdaptive.cpp | 66 ++++++++++++++++++++----------- src/libslic3r/SlicingAdaptive.hpp | 32 +++++++++++---- src/slic3r/GUI/GLCanvas3D.cpp | 12 +++--- 5 files changed, 104 insertions(+), 41 deletions(-) diff --git a/src/libslic3r/Slicing.cpp b/src/libslic3r/Slicing.cpp index 1c1a1159f..a82bbc72a 100644 --- a/src/libslic3r/Slicing.cpp +++ b/src/libslic3r/Slicing.cpp @@ -224,24 +224,38 @@ std::vector layer_height_profile_from_ranges( // Based on the work of @platsch // Fill layer_height_profile by heights ensuring a prescribed maximum cusp height. +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE +std::vector layer_height_profile_adaptive( + const SlicingParameters& slicing_params, + const ModelObject& object) +#else std::vector layer_height_profile_adaptive( const SlicingParameters &slicing_params, const t_layer_config_ranges & /* layer_config_ranges */, const ModelVolumePtrs &volumes) +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE { +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE // 1) Initialize the SlicingAdaptive class with the object meshes. SlicingAdaptive as; as.set_slicing_parameters(slicing_params); - for (const ModelVolume *volume : volumes) + as.set_object(object); +#else + // 1) Initialize the SlicingAdaptive class with the object meshes. + SlicingAdaptive as; + as.set_slicing_parameters(slicing_params); + for (const ModelVolume* volume : volumes) if (volume->is_model_part()) as.add_mesh(&volume->mesh()); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + as.prepare(); // 2) Generate layers using the algorithm of @platsch // loop until we have at least one layer and the max slice_z reaches the object height //FIXME make it configurable // Cusp value: A maximum allowed distance from a corner of a rectangular extrusion to a chrodal line, in mm. - const coordf_t cusp_value = 0.2; // $self->config->get_value('cusp_value'); + const double cusp_value = 0.2; // $self->config->get_value('cusp_value'); std::vector layer_height_profile; layer_height_profile.push_back(0.); @@ -250,14 +264,14 @@ std::vector layer_height_profile_adaptive( layer_height_profile.push_back(slicing_params.first_object_layer_height); layer_height_profile.push_back(slicing_params.first_object_layer_height); } - coordf_t slice_z = slicing_params.first_object_layer_height; - coordf_t height = slicing_params.first_object_layer_height; + double slice_z = slicing_params.first_object_layer_height; + double height = slicing_params.first_object_layer_height; int current_facet = 0; while ((slice_z - height) <= slicing_params.object_print_z_height()) { height = 999; // Slic3r::debugf "\n Slice layer: %d\n", $id; // determine next layer height - coordf_t cusp_height = as.cusp_height(slice_z, cusp_value, current_facet); + double cusp_height = as.cusp_height((float)slice_z, (float)cusp_value, current_facet); // check for horizontal features and object size /* if($self->config->get_value('match_horizontal_surfaces')) { diff --git a/src/libslic3r/Slicing.hpp b/src/libslic3r/Slicing.hpp index 064363ec2..250d7baeb 100644 --- a/src/libslic3r/Slicing.hpp +++ b/src/libslic3r/Slicing.hpp @@ -18,8 +18,12 @@ namespace Slic3r class PrintConfig; class PrintObjectConfig; +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE +class ModelObject; +#else class ModelVolume; typedef std::vector ModelVolumePtrs; +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE // Parameters to guide object slicing and support generation. // The slicing parameters account for a raft and whether the 1st object layer is printed with a normal or a bridging flow @@ -138,11 +142,16 @@ extern std::vector layer_height_profile_from_ranges( const SlicingParameters &slicing_params, const t_layer_config_ranges &layer_config_ranges); +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE +extern std::vector layer_height_profile_adaptive( + const SlicingParameters& slicing_params, + const ModelObject& object); +#else extern std::vector layer_height_profile_adaptive( const SlicingParameters &slicing_params, const t_layer_config_ranges &layer_config_ranges, const ModelVolumePtrs &volumes); - +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE enum LayerHeightEditActionType : unsigned int { LAYER_HEIGHT_EDIT_ACTION_INCREASE = 0, diff --git a/src/libslic3r/SlicingAdaptive.cpp b/src/libslic3r/SlicingAdaptive.cpp index ad03b550b..a38752fbc 100644 --- a/src/libslic3r/SlicingAdaptive.cpp +++ b/src/libslic3r/SlicingAdaptive.cpp @@ -1,16 +1,22 @@ #include "libslic3r.h" +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE +#include "Model.hpp" +#else #include "TriangleMesh.hpp" +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE #include "SlicingAdaptive.hpp" namespace Slic3r { +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE void SlicingAdaptive::clear() { - m_meshes.clear(); + m_meshes.clear(); m_faces.clear(); m_face_normal_z.clear(); } +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE std::pair face_z_span(const stl_facet *f) { @@ -21,21 +27,38 @@ std::pair face_z_span(const stl_facet *f) void SlicingAdaptive::prepare() { - // 1) Collect faces of all meshes. - int nfaces_total = 0; - for (std::vector::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh) +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + if (m_object == nullptr) + return; + + m_faces.clear(); + m_face_normal_z.clear(); + + m_mesh = m_object->raw_mesh(); + const ModelInstance* first_instance = m_object->instances.front(); + m_mesh.transform(first_instance->get_matrix(), first_instance->is_left_handed()); + + // 1) Collect faces from mesh. + m_faces.reserve(m_mesh.stl.stats.number_of_facets); + for (const stl_facet& face : m_mesh.stl.facet_start) + m_faces.emplace_back(&face); +#else + // 1) Collect faces of all meshes. + int nfaces_total = 0; + for (std::vector::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh) nfaces_total += (*it_mesh)->stl.stats.number_of_facets; - m_faces.reserve(nfaces_total); - for (std::vector::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh) - for (const stl_facet &face : (*it_mesh)->stl.facet_start) - m_faces.emplace_back(&face); + m_faces.reserve(nfaces_total); + for (std::vector::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh) + for (const stl_facet& face : (*it_mesh)->stl.facet_start) + m_faces.emplace_back(&face); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE // 2) Sort faces lexicographically by their Z span. std::sort(m_faces.begin(), m_faces.end(), [](const stl_facet *f1, const stl_facet *f2) { - std::pair span1 = face_z_span(f1); + std::pair span1 = face_z_span(f1); std::pair span2 = face_z_span(f2); - return span1 < span2; - }); + return span1 < span2; + }); // 3) Generate Z components of the facet normals. m_face_normal_z.assign(m_faces.size(), 0.f); @@ -45,14 +68,14 @@ void SlicingAdaptive::prepare() float SlicingAdaptive::cusp_height(float z, float cusp_value, int ¤t_facet) { - float height = m_slicing_params.max_layer_height; + float height = (float)m_slicing_params.max_layer_height; bool first_hit = false; // find all facets intersecting the slice-layer int ordered_id = current_facet; for (; ordered_id < int(m_faces.size()); ++ ordered_id) { - std::pair zspan = face_z_span(m_faces[ordered_id]); - // facet's minimum is higher than slice_z -> end loop + std::pair zspan = face_z_span(m_faces[ordered_id]); + // facet's minimum is higher than slice_z -> end loop if (zspan.first >= z) break; // facet's maximum is higher than slice_z -> store the first event for next cusp_height call to begin at this point @@ -77,8 +100,8 @@ float SlicingAdaptive::cusp_height(float z, float cusp_value, int ¤t_facet // check for sloped facets inside the determined layer and correct height if necessary if (height > m_slicing_params.min_layer_height) { for (; ordered_id < int(m_faces.size()); ++ ordered_id) { - std::pair zspan = face_z_span(m_faces[ordered_id]); - // facet's minimum is higher than slice_z + height -> end loop + std::pair zspan = face_z_span(m_faces[ordered_id]); + // facet's minimum is higher than slice_z + height -> end loop if (zspan.first >= z + height) break; @@ -122,19 +145,18 @@ float SlicingAdaptive::cusp_height(float z, float cusp_value, int ¤t_facet float SlicingAdaptive::horizontal_facet_distance(float z) { for (size_t i = 0; i < m_faces.size(); ++ i) { - std::pair zspan = face_z_span(m_faces[i]); - // facet's minimum is higher than max forward distance -> end loop + std::pair zspan = face_z_span(m_faces[i]); + // facet's minimum is higher than max forward distance -> end loop if (zspan.first > z + m_slicing_params.max_layer_height) break; // min_z == max_z -> horizontal facet - if (zspan.first > z && zspan.first == zspan.second) + if ((zspan.first > z) && (zspan.first == zspan.second)) return zspan.first - z; } // objects maximum? - return (z + m_slicing_params.max_layer_height > m_slicing_params.object_print_z_height()) ? - std::max(m_slicing_params.object_print_z_height() - z, 0.f) : - m_slicing_params.max_layer_height; + return (z + (float)m_slicing_params.max_layer_height > (float)m_slicing_params.object_print_z_height()) ? + std::max((float)m_slicing_params.object_print_z_height() - z, 0.f) : (float)m_slicing_params.max_layer_height; } }; // namespace Slic3r diff --git a/src/libslic3r/SlicingAdaptive.hpp b/src/libslic3r/SlicingAdaptive.hpp index bfd081d81..eccb298d0 100644 --- a/src/libslic3r/SlicingAdaptive.hpp +++ b/src/libslic3r/SlicingAdaptive.hpp @@ -5,29 +5,47 @@ #include "Slicing.hpp" #include "admesh/stl.h" +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE +#include "TriangleMesh.hpp" +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE namespace Slic3r { +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE +class ModelVolume; +#else class TriangleMesh; +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE class SlicingAdaptive { public: - void clear(); - void set_slicing_parameters(SlicingParameters params) { m_slicing_params = params; } - void add_mesh(const TriangleMesh *mesh) { m_meshes.push_back(mesh); } - void prepare(); +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + void clear(); +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + void set_slicing_parameters(SlicingParameters params) { m_slicing_params = params; } +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + void set_object(const ModelObject& object) { m_object = &object; } +#else + void add_mesh(const TriangleMesh* mesh) { m_meshes.push_back(mesh); } +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + void prepare(); float cusp_height(float z, float cusp_value, int ¤t_facet); float horizontal_facet_distance(float z); protected: SlicingParameters m_slicing_params; - std::vector m_meshes; - // Collected faces of all meshes, sorted by raising Z of the bottom most face. +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + const ModelObject* m_object; + TriangleMesh m_mesh; +#else + std::vector m_meshes; +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + // Collected faces of all meshes, sorted by raising Z of the bottom most face. std::vector m_faces; - // Z component of face normals, normalized. + // Z component of face normals, normalized. std::vector m_face_normal_z; }; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 7b1dfb89a..9647d252a 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -266,13 +266,13 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const imgui.text(_(L("Increase/decrease edit area"))); ImGui::Separator(); - if (imgui.button(_(L("Reset")))) - wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), SimpleEvent(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE)); - - ImGui::SameLine(); if (imgui.button(_(L("Adaptive")))) wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), SimpleEvent(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE)); + ImGui::SameLine(); + if (imgui.button(_(L("Reset")))) + wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), SimpleEvent(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE)); + imgui.end(); ImGui::PopStyleVar(); @@ -577,8 +577,8 @@ void GLCanvas3D::LayersEditing::reset_layer_height_profile(GLCanvas3D& canvas) #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE void GLCanvas3D::LayersEditing::adaptive_layer_height_profile(GLCanvas3D& canvas) { - const_cast(m_model_object)->layer_height_profile.clear(); - m_layer_height_profile = layer_height_profile_adaptive(*m_slicing_parameters, m_model_object->layer_config_ranges, m_model_object->volumes); + m_layer_height_profile = layer_height_profile_adaptive(*m_slicing_parameters, *m_model_object); + const_cast(m_model_object)->layer_height_profile = m_layer_height_profile; m_layers_texture.valid = false; canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); } From 6eee31bf5a28264a48982eb32bfd428e7469af3d Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 13 Nov 2019 15:06:17 +0100 Subject: [PATCH 46/67] ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE -> Disabled unused code --- src/libslic3r/SlicingAdaptive.cpp | 2 ++ src/libslic3r/SlicingAdaptive.hpp | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/SlicingAdaptive.cpp b/src/libslic3r/SlicingAdaptive.cpp index a38752fbc..e64012553 100644 --- a/src/libslic3r/SlicingAdaptive.cpp +++ b/src/libslic3r/SlicingAdaptive.cpp @@ -140,6 +140,7 @@ float SlicingAdaptive::cusp_height(float z, float cusp_value, int ¤t_facet return height; } +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE // Returns the distance to the next horizontal facet in Z-dir // to consider horizontal object features in slice thickness float SlicingAdaptive::horizontal_facet_distance(float z) @@ -158,5 +159,6 @@ float SlicingAdaptive::horizontal_facet_distance(float z) return (z + (float)m_slicing_params.max_layer_height > (float)m_slicing_params.object_print_z_height()) ? std::max((float)m_slicing_params.object_print_z_height() - z, 0.f) : (float)m_slicing_params.max_layer_height; } +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE }; // namespace Slic3r diff --git a/src/libslic3r/SlicingAdaptive.hpp b/src/libslic3r/SlicingAdaptive.hpp index eccb298d0..1d2996986 100644 --- a/src/libslic3r/SlicingAdaptive.hpp +++ b/src/libslic3r/SlicingAdaptive.hpp @@ -32,7 +32,9 @@ public: #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE void prepare(); float cusp_height(float z, float cusp_value, int ¤t_facet); - float horizontal_facet_distance(float z); +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + float horizontal_facet_distance(float z); +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE protected: SlicingParameters m_slicing_params; From 5baffdb9c20f55d5bc95b48a5e6355258a080ce9 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 14 Nov 2019 10:22:48 +0100 Subject: [PATCH 47/67] ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE -> Customizable cusp value --- src/libslic3r/Slicing.cpp | 13 +++++-------- src/libslic3r/Slicing.hpp | 4 ++-- src/slic3r/GUI/GLCanvas3D.cpp | 20 ++++++++++++++------ src/slic3r/GUI/GLCanvas3D.hpp | 8 +++++--- src/slic3r/GUI/Plater.cpp | 2 +- 5 files changed, 27 insertions(+), 20 deletions(-) diff --git a/src/libslic3r/Slicing.cpp b/src/libslic3r/Slicing.cpp index a82bbc72a..b7816fd04 100644 --- a/src/libslic3r/Slicing.cpp +++ b/src/libslic3r/Slicing.cpp @@ -225,9 +225,8 @@ std::vector layer_height_profile_from_ranges( // Based on the work of @platsch // Fill layer_height_profile by heights ensuring a prescribed maximum cusp height. #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE -std::vector layer_height_profile_adaptive( - const SlicingParameters& slicing_params, - const ModelObject& object) +std::vector layer_height_profile_adaptive(const SlicingParameters& slicing_params, + const ModelObject& object, float cusp_value) #else std::vector layer_height_profile_adaptive( const SlicingParameters &slicing_params, @@ -253,11 +252,8 @@ std::vector layer_height_profile_adaptive( // 2) Generate layers using the algorithm of @platsch // loop until we have at least one layer and the max slice_z reaches the object height - //FIXME make it configurable - // Cusp value: A maximum allowed distance from a corner of a rectangular extrusion to a chrodal line, in mm. - const double cusp_value = 0.2; // $self->config->get_value('cusp_value'); - std::vector layer_height_profile; + std::vector layer_height_profile; layer_height_profile.push_back(0.); layer_height_profile.push_back(slicing_params.first_object_layer_height); if (slicing_params.first_object_layer_height_fixed()) { @@ -271,7 +267,8 @@ std::vector layer_height_profile_adaptive( height = 999; // Slic3r::debugf "\n Slice layer: %d\n", $id; // determine next layer height - double cusp_height = as.cusp_height((float)slice_z, (float)cusp_value, current_facet); + double cusp_height = as.cusp_height((float)slice_z, cusp_value, current_facet); + // check for horizontal features and object size /* if($self->config->get_value('match_horizontal_surfaces')) { diff --git a/src/libslic3r/Slicing.hpp b/src/libslic3r/Slicing.hpp index 250d7baeb..e25cbd326 100644 --- a/src/libslic3r/Slicing.hpp +++ b/src/libslic3r/Slicing.hpp @@ -143,9 +143,9 @@ extern std::vector layer_height_profile_from_ranges( const t_layer_config_ranges &layer_config_ranges); #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE -extern std::vector layer_height_profile_adaptive( +extern std::vector layer_height_profile_adaptive( const SlicingParameters& slicing_params, - const ModelObject& object); + const ModelObject& object, float cusp_value); #else extern std::vector layer_height_profile_adaptive( const SlicingParameters &slicing_params, diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 9647d252a..8ef0eaa13 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -130,6 +130,7 @@ GLCanvas3D::LayersEditing::LayersEditing() , m_object_max_z(0.f) , m_slicing_parameters(nullptr) , m_layer_height_profile_modified(false) + , m_adaptive_cusp(0.2f) , state(Unknown) , band_width(2.0f) , strength(0.005f) @@ -267,9 +268,16 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const ImGui::Separator(); if (imgui.button(_(L("Adaptive")))) - wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), SimpleEvent(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE)); + wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), Event(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, m_adaptive_cusp)); ImGui::SameLine(); + imgui.text(_(L("Cusp (mm)"))); + ImGui::SameLine(); + ImGui::PushItemWidth(100.0f); + m_adaptive_cusp = std::min(m_adaptive_cusp, (float)m_slicing_parameters->max_layer_height); + ImGui::SliderFloat("", &m_adaptive_cusp, 0.0f, (float)m_slicing_parameters->max_layer_height, "%.2f"); + + ImGui::Separator(); if (imgui.button(_(L("Reset")))) wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), SimpleEvent(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE)); @@ -575,9 +583,9 @@ void GLCanvas3D::LayersEditing::reset_layer_height_profile(GLCanvas3D& canvas) } #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE -void GLCanvas3D::LayersEditing::adaptive_layer_height_profile(GLCanvas3D& canvas) +void GLCanvas3D::LayersEditing::adaptive_layer_height_profile(GLCanvas3D& canvas, float cusp) { - m_layer_height_profile = layer_height_profile_adaptive(*m_slicing_parameters, *m_model_object); + m_layer_height_profile = layer_height_profile_adaptive(*m_slicing_parameters, *m_model_object, cusp); const_cast(m_model_object)->layer_height_profile = m_layer_height_profile; m_layers_texture.valid = false; canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); @@ -1210,7 +1218,7 @@ wxDEFINE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent); #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE wxDEFINE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent); -wxDEFINE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, SimpleEvent); +wxDEFINE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, Event); #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE #if ENABLE_THUMBNAIL_GENERATOR @@ -1522,9 +1530,9 @@ void GLCanvas3D::reset_layer_height_profile() m_dirty = true; } -void GLCanvas3D::adaptive_layer_height_profile() +void GLCanvas3D::adaptive_layer_height_profile(float cusp) { - m_layers_editing.adaptive_layer_height_profile(*this); + m_layers_editing.adaptive_layer_height_profile(*this, cusp); m_layers_editing.state = LayersEditing::Completed; m_dirty = true; } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index d17174628..d1369d086 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -106,7 +106,7 @@ wxDECLARE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent); #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE wxDECLARE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent); -wxDECLARE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, SimpleEvent); +wxDECLARE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, Event); #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE class GLCanvas3D @@ -179,6 +179,8 @@ private: std::vector m_layer_height_profile; bool m_layer_height_profile_modified; + mutable float m_adaptive_cusp; + class LayersTexture { public: @@ -226,7 +228,7 @@ private: void accept_changes(GLCanvas3D& canvas); void reset_layer_height_profile(GLCanvas3D& canvas); #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE - void adaptive_layer_height_profile(GLCanvas3D& canvas); + void adaptive_layer_height_profile(GLCanvas3D& canvas, float cusp); #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE static float get_cursor_z_relative(const GLCanvas3D& canvas); @@ -523,7 +525,7 @@ public: #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE void reset_layer_height_profile(); - void adaptive_layer_height_profile(); + void adaptive_layer_height_profile(float cusp); #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE bool is_reload_delayed() const; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index ac9c5cc4b..e2b15602c 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2091,7 +2091,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) view3D_canvas->Bind(EVT_GLCANVAS_REDO, [this](SimpleEvent&) { this->redo(); }); #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE view3D_canvas->Bind(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, [this](SimpleEvent&) { this->view3D->get_canvas3d()->reset_layer_height_profile(); }); - view3D_canvas->Bind(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, [this](SimpleEvent&) { this->view3D->get_canvas3d()->adaptive_layer_height_profile(); }); + view3D_canvas->Bind(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, [this](Event& evt) { this->view3D->get_canvas3d()->adaptive_layer_height_profile(evt.data); }); #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE // 3DScene/Toolbar: From f548a4d7d6fe328cf16f8b901758d60b8d94c2e3 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 15 Nov 2019 15:49:07 +0100 Subject: [PATCH 48/67] ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE -> Profile smoothing using gaussian blur --- src/libslic3r/Slicing.cpp | 74 +++++++++++++++++++++++++++++++++- src/libslic3r/Slicing.hpp | 5 +++ src/libslic3r/Technologies.hpp | 1 + src/slic3r/GUI/GLCanvas3D.cpp | 52 +++++++++++++++++++++++- src/slic3r/GUI/GLCanvas3D.hpp | 14 +++++++ src/slic3r/GUI/Plater.cpp | 3 ++ 6 files changed, 145 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/Slicing.cpp b/src/libslic3r/Slicing.cpp index b7816fd04..0d3b3af38 100644 --- a/src/libslic3r/Slicing.cpp +++ b/src/libslic3r/Slicing.cpp @@ -252,6 +252,9 @@ std::vector layer_height_profile_adaptive( // 2) Generate layers using the algorithm of @platsch // loop until we have at least one layer and the max slice_z reaches the object height +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + double cusp_value = 0.2; +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE std::vector layer_height_profile; layer_height_profile.push_back(0.); @@ -264,7 +267,7 @@ std::vector layer_height_profile_adaptive( double height = slicing_params.first_object_layer_height; int current_facet = 0; while ((slice_z - height) <= slicing_params.object_print_z_height()) { - height = 999; + height = 999.0; // Slic3r::debugf "\n Slice layer: %d\n", $id; // determine next layer height double cusp_height = as.cusp_height((float)slice_z, cusp_value, current_facet); @@ -318,7 +321,7 @@ std::vector layer_height_profile_adaptive( layer_height_profile.push_back(height); } - coordf_t last = std::max(slicing_params.first_object_layer_height, layer_height_profile[layer_height_profile.size() - 2]); + double last = std::max(slicing_params.first_object_layer_height, layer_height_profile[layer_height_profile.size() - 2]); layer_height_profile.push_back(last); layer_height_profile.push_back(slicing_params.first_object_layer_height); layer_height_profile.push_back(slicing_params.object_print_z_height()); @@ -327,6 +330,73 @@ std::vector layer_height_profile_adaptive( return layer_height_profile; } +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING +std::vector smooth_height_profile(const std::vector& profile, const SlicingParameters& slicing_params, + unsigned int radius) +{ + auto gauss_blur = [] (const std::vector& profile, unsigned int radius) -> std::vector { + auto gauss_kernel = [] (unsigned int radius) -> std::vector { + unsigned int size = 2 * radius + 1; + std::vector ret; + ret.reserve(size); + + // Reworked from static inline int getGaussianKernelSize(float sigma) taken from opencv-4.1.2\modules\features2d\src\kaze\AKAZEFeatures.cpp + double sigma = 0.3 * (double)(radius - 1) + 0.8; + double two_sq_sigma = 2.0 * sigma * sigma; + double inv_root_two_pi_sq_sigma = 1.0 / ::sqrt(M_PI * two_sq_sigma); + + for (unsigned int i = 0; i < size; ++i) + { + double x = (double)i - (double)radius; + ret.push_back(inv_root_two_pi_sq_sigma * ::exp(-x * x / two_sq_sigma)); + } + + return ret; + }; + + std::vector ret; + size_t size = profile.size(); + ret.reserve(size); + std::vector kernel = gauss_kernel(radius); + + for (size_t i = 0; i < size; ++i) + { + unsigned int id = 0; + double value = 0.0; + for (int j = (int)(i - radius); j <= (int)(i + radius); ++j) + { + if ((0 <= j) && (j < size)) + value += kernel[id] * profile[j]; + + ++id; + } + ret.push_back(value); + } + + return ret; + }; + + std::vector ret = profile; + + std::vector heights; + size_t heights_size = ret.size() / 2; + heights.reserve(heights_size); + for (size_t i = 0; i < heights_size; ++i) + { + heights.push_back(ret[i * 2 + 1]); + } + + heights = gauss_blur(heights, std::max(radius, (unsigned int)1)); + + for (size_t i = 0; i < heights_size; ++i) + { + ret[i * 2 + 1] = clamp(slicing_params.min_layer_height, slicing_params.max_layer_height, heights[i]); + } + + return ret; +} +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING + void adjust_layer_height_profile( const SlicingParameters &slicing_params, std::vector &layer_height_profile, diff --git a/src/libslic3r/Slicing.hpp b/src/libslic3r/Slicing.hpp index e25cbd326..545857216 100644 --- a/src/libslic3r/Slicing.hpp +++ b/src/libslic3r/Slicing.hpp @@ -146,6 +146,11 @@ extern std::vector layer_height_profile_from_ranges( extern std::vector layer_height_profile_adaptive( const SlicingParameters& slicing_params, const ModelObject& object, float cusp_value); + +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING +extern std::vector smooth_height_profile(const std::vector& profile, const SlicingParameters& slicing_params, + unsigned int radius); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING #else extern std::vector layer_height_profile_adaptive( const SlicingParameters &slicing_params, diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index f8db1b7b3..465f5dd33 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -44,5 +44,6 @@ // Enable adaptive layer height profile #define ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE (1 && ENABLE_2_2_0_ALPHA1) +#define ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING (1 && ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE) #endif // _technologies_h_ diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 8ef0eaa13..d0cd1fbeb 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -130,7 +130,12 @@ GLCanvas3D::LayersEditing::LayersEditing() , m_object_max_z(0.f) , m_slicing_parameters(nullptr) , m_layer_height_profile_modified(false) +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE , m_adaptive_cusp(0.2f) +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING + , m_smooth_radius(5) +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE , state(Unknown) , band_width(2.0f) , strength(0.005f) @@ -227,9 +232,10 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const float canvas_w = (float)cnv_size.get_width(); float canvas_h = (float)cnv_size.get_height(); + const float scale_gl = wxGetApp().mainframe->scale_factor(); ImGuiWrapper& imgui = *wxGetApp().imgui(); - imgui.set_next_window_pos(canvas_w - THICKNESS_BAR_WIDTH, canvas_h, ImGuiCond_Always, 1.0f, 1.0f); + imgui.set_next_window_pos(canvas_w - scale_gl * THICKNESS_BAR_WIDTH, canvas_h, ImGuiCond_Always, 1.0f, 1.0f); imgui.set_next_window_bg_alpha(0.5f); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); @@ -271,12 +277,30 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), Event(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, m_adaptive_cusp)); ImGui::SameLine(); + float text_align = ImGui::GetCursorPosX(); imgui.text(_(L("Cusp (mm)"))); ImGui::SameLine(); - ImGui::PushItemWidth(100.0f); + float widget_align = ImGui::GetCursorPosX(); + ImGui::PushItemWidth(120.0f); m_adaptive_cusp = std::min(m_adaptive_cusp, (float)m_slicing_parameters->max_layer_height); ImGui::SliderFloat("", &m_adaptive_cusp, 0.0f, (float)m_slicing_parameters->max_layer_height, "%.2f"); +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING + ImGui::Separator(); + if (imgui.button(_(L("Smooth")))) + wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), Event(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, m_smooth_radius)); + + ImGui::SameLine(); + ImGui::SetCursorPosX(text_align); + imgui.text(_(L("Radius"))); + ImGui::SameLine(); + ImGui::PushItemWidth(120.0f); + ImGui::SetCursorPosX(widget_align); + int radius = (int)m_smooth_radius; + if (ImGui::SliderInt("##1", &radius, 1, 10)) + m_smooth_radius = (unsigned int)radius; +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING + ImGui::Separator(); if (imgui.button(_(L("Reset")))) wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), SimpleEvent(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE)); @@ -590,6 +614,17 @@ void GLCanvas3D::LayersEditing::adaptive_layer_height_profile(GLCanvas3D& canvas m_layers_texture.valid = false; canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); } + +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING +void GLCanvas3D::LayersEditing::smooth_layer_height_profile(GLCanvas3D& canvas, unsigned int radius) +{ + m_layer_height_profile = smooth_height_profile(m_layer_height_profile, *m_slicing_parameters, radius); + const_cast(m_model_object)->layer_height_profile = m_layer_height_profile; + m_layers_texture.valid = false; + canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); +} +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING + #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE void GLCanvas3D::LayersEditing::generate_layer_height_texture() @@ -1219,6 +1254,9 @@ wxDEFINE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent); #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE wxDEFINE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, Event); +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING +wxDEFINE_EVENT(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, Event); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE #if ENABLE_THUMBNAIL_GENERATOR @@ -1536,6 +1574,16 @@ void GLCanvas3D::adaptive_layer_height_profile(float cusp) m_layers_editing.state = LayersEditing::Completed; m_dirty = true; } + +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING +void GLCanvas3D::smooth_layer_height_profile(unsigned int radius) +{ + m_layers_editing.smooth_layer_height_profile(*this, radius); + m_layers_editing.state = LayersEditing::Completed; + m_dirty = true; +} +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING + #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE bool GLCanvas3D::is_reload_delayed() const diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index d1369d086..d57a5c8d6 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -107,6 +107,9 @@ wxDECLARE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent); #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE wxDECLARE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, Event); +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING +wxDECLARE_EVENT(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, Event); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE class GLCanvas3D @@ -179,7 +182,12 @@ private: std::vector m_layer_height_profile; bool m_layer_height_profile_modified; +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE mutable float m_adaptive_cusp; +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING + mutable unsigned int m_smooth_radius; +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE class LayersTexture { @@ -229,6 +237,9 @@ private: void reset_layer_height_profile(GLCanvas3D& canvas); #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE void adaptive_layer_height_profile(GLCanvas3D& canvas, float cusp); +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING + void smooth_layer_height_profile(GLCanvas3D& canvas, unsigned int radius); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE static float get_cursor_z_relative(const GLCanvas3D& canvas); @@ -526,6 +537,9 @@ public: #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE void reset_layer_height_profile(); void adaptive_layer_height_profile(float cusp); +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING + void smooth_layer_height_profile(unsigned int radius); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE bool is_reload_delayed() const; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index e2b15602c..3c93486d8 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2092,6 +2092,9 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE view3D_canvas->Bind(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, [this](SimpleEvent&) { this->view3D->get_canvas3d()->reset_layer_height_profile(); }); view3D_canvas->Bind(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, [this](Event& evt) { this->view3D->get_canvas3d()->adaptive_layer_height_profile(evt.data); }); +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING + view3D_canvas->Bind(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, [this](Event& evt) { this->view3D->get_canvas3d()->smooth_layer_height_profile(evt.data); }); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE // 3DScene/Toolbar: From bbb519ad0eceba5a2a6812b853fa2192bd8c9594 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Sat, 16 Nov 2019 08:22:39 +0100 Subject: [PATCH 49/67] Fixed a typo in MeshUtils.hpp --- src/libslic3r/Utils.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp index 71e896745..e5fae485a 100644 --- a/src/libslic3r/Utils.hpp +++ b/src/libslic3r/Utils.hpp @@ -194,7 +194,7 @@ inline typename CONTAINER_TYPE::size_type next_idx_modulo(typename CONTAINER_TYP } template -inline typename const CONTAINER_TYPE::value_type& prev_value_modulo(typename CONTAINER_TYPE::size_type idx, const CONTAINER_TYPE &container) +inline const typename CONTAINER_TYPE::value_type& prev_value_modulo(typename CONTAINER_TYPE::size_type idx, const CONTAINER_TYPE &container) { return container[prev_idx_modulo(idx, container.size())]; } @@ -206,7 +206,7 @@ inline typename CONTAINER_TYPE::value_type& prev_value_modulo(typename CONTAINER } template -inline typename const CONTAINER_TYPE::value_type& next_value_modulo(typename CONTAINER_TYPE::size_type idx, const CONTAINER_TYPE &container) +inline const typename CONTAINER_TYPE::value_type& next_value_modulo(typename CONTAINER_TYPE::size_type idx, const CONTAINER_TYPE &container) { return container[next_idx_modulo(idx, container.size())]; } From a1aa343416d402bb2e7f28d131503c3067e534df Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Sat, 16 Nov 2019 09:04:06 +0100 Subject: [PATCH 50/67] Follow-up of b2a6a07d723e11277f5ce5f78d67dec5dcb2c1f0 -> Fixed code to render supports and pad into thumbnails for sla printer (currently commented out) --- src/slic3r/GUI/Plater.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 221eff7c2..f04e1e17f 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3438,11 +3438,13 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt) // for ptSLA generate the thumbnail after supports and pad have been calculated to have them rendered if ((this->printer_technology == ptSLA) && (evt.status.percent == -3)) { + const std::vector& thumbnail_sizes = this->background_process.current_print()->full_print_config().option("thumbnails")->values; this->thumbnail_data.clear(); - for (const std::pair& size : THUMBNAIL_SIZE_SLA) + for (const Vec2d &sized : thumbnail_sizes) { this->thumbnail_data.push_back(ThumbnailData()); - generate_thumbnail(this->thumbnail_data.back(), size.first, size.second, true, false, false); + Point size(sized); // round to ints + generate_thumbnail(this->thumbnail_data.back(), size.x(), size.y(), true, false, false); } } #endif // ENABLE_THUMBNAIL_GENERATOR From 7baefdf79dea3bb4ab88cb5ea324354bed5eacde Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 18 Nov 2019 10:04:32 +0100 Subject: [PATCH 51/67] Fix for deps patching --- deps/deps-unix-common.cmake | 13 ++++++------- deps/deps-windows.cmake | 11 ++++++----- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/deps/deps-unix-common.cmake b/deps/deps-unix-common.cmake index 74582f601..944d2c06d 100644 --- a/deps/deps-unix-common.cmake +++ b/deps/deps-unix-common.cmake @@ -61,8 +61,8 @@ ExternalProject_Add(dep_qhull -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local ${DEP_CMAKE_OPTS} - UPDATE_COMMAND "" - PATCH_COMMAND patch -p1 < ${CMAKE_CURRENT_SOURCE_DIR}/qhull-mods.patch + PATCH_COMMAND ${GIT_EXECUTABLE} checkout -f -- . && git clean -df && + ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/qhull-mods.patch ) ExternalProject_Add(dep_blosc @@ -80,8 +80,8 @@ ExternalProject_Add(dep_blosc -DBUILD_TESTS=OFF -DBUILD_BENCHMARKS=OFF -DPREFER_EXTERNAL_ZLIB=ON - UPDATE_COMMAND "" - PATCH_COMMAND ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/blosc-mods.patch + PATCH_COMMAND ${GIT_EXECUTABLE} checkout -f -- . && git clean -df && + ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/blosc-mods.patch ) ExternalProject_Add(dep_openexr @@ -96,7 +96,6 @@ ExternalProject_Add(dep_openexr -DPYILMBASE_ENABLE:BOOL=OFF -DOPENEXR_VIEWERS_ENABLE:BOOL=OFF -DOPENEXR_BUILD_UTILS:BOOL=OFF - UPDATE_COMMAND "" ) ExternalProject_Add(dep_openvdb @@ -116,6 +115,6 @@ ExternalProject_Add(dep_openvdb -DOPENVDB_CORE_STATIC=ON -DTBB_STATIC=ON -DOPENVDB_BUILD_VDB_PRINT=ON - UPDATE_COMMAND "" - PATCH_COMMAND ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/openvdb-mods.patch + PATCH_COMMAND PATCH_COMMAND ${GIT_EXECUTABLE} checkout -f -- . && git clean -df && + ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/openvdb-mods.patch ) \ No newline at end of file diff --git a/deps/deps-windows.cmake b/deps/deps-windows.cmake index 4aae07d4a..3affbaefa 100644 --- a/deps/deps-windows.cmake +++ b/deps/deps-windows.cmake @@ -226,7 +226,8 @@ ExternalProject_Add(dep_qhull -DBUILD_SHARED_LIBS=OFF -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_DEBUG_POSTFIX=d - UPDATE_COMMAND "" + PATCH_COMMAND PATCH_COMMAND ${GIT_EXECUTABLE} checkout -f -- . && git clean -df && + ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/qhull-mods.patch BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj INSTALL_COMMAND "" ) @@ -287,8 +288,8 @@ ExternalProject_Add(dep_blosc -DPREFER_EXTERNAL_ZLIB=ON -DBLOSC_IS_SUBPROJECT:BOOL=ON -DBLOSC_INSTALL:BOOL=ON - UPDATE_COMMAND "" - PATCH_COMMAND ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/blosc-mods.patch + PATCH_COMMAND ${GIT_EXECUTABLE} checkout -f -- . && git clean -df && + ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/blosc-mods.patch BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj INSTALL_COMMAND "" ) @@ -339,8 +340,8 @@ ExternalProject_Add(dep_openvdb -DTBB_STATIC=ON -DOPENVDB_BUILD_VDB_PRINT=ON BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj - UPDATE_COMMAND "" - PATCH_COMMAND ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/openvdb-mods.patch + PATCH_COMMAND ${GIT_EXECUTABLE} checkout -f -- . && git clean -df && + ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/openvdb-mods.patch INSTALL_COMMAND "" ) From ab735bdc5445a77aa0fb69f22c4cd9565b382124 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 18 Nov 2019 09:35:26 +0100 Subject: [PATCH 52/67] Follow up: fixing patches on windows --- deps/blosc-mods.patch | 3 ++- deps/deps-windows.cmake | 5 +---- deps/openvdb-mods.patch | 3 ++- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/deps/blosc-mods.patch b/deps/blosc-mods.patch index 9a91b4974..9b1b9cb27 100644 --- a/deps/blosc-mods.patch +++ b/deps/blosc-mods.patch @@ -1,8 +1,9 @@ -From 5669891dfaaa4c814f3ec667ca6bf4e693aea978 Mon Sep 17 00:00:00 2001 +From 7cf6c014a36f1712efbdbe9bc52d2d4922b54673 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 30 Oct 2019 12:54:52 +0100 Subject: [PATCH] Blosc 1.17 fixes and cmake config script +Signed-off-by: tamasmeszaros --- CMakeLists.txt | 105 +++++++++++++++++----------------- blosc/CMakeLists.txt | 118 +++++++++------------------------------ diff --git a/deps/deps-windows.cmake b/deps/deps-windows.cmake index 3affbaefa..1b978bdde 100644 --- a/deps/deps-windows.cmake +++ b/deps/deps-windows.cmake @@ -226,8 +226,6 @@ ExternalProject_Add(dep_qhull -DBUILD_SHARED_LIBS=OFF -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_DEBUG_POSTFIX=d - PATCH_COMMAND PATCH_COMMAND ${GIT_EXECUTABLE} checkout -f -- . && git clean -df && - ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/qhull-mods.patch BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj INSTALL_COMMAND "" ) @@ -311,7 +309,6 @@ ExternalProject_Add(dep_openexr -DPYILMBASE_ENABLE:BOOL=OFF -DOPENEXR_VIEWERS_ENABLE:BOOL=OFF -DOPENEXR_BUILD_UTILS:BOOL=OFF - UPDATE_COMMAND "" BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj INSTALL_COMMAND "" ) @@ -324,7 +321,7 @@ ExternalProject_Add(dep_openvdb #URL_HASH SHA256=dc337399dce8e1c9f21f20e97b1ce7e4933cb0a63bb3b8b734d8fcc464aa0c48 GIT_REPOSITORY https://github.com/AcademySoftwareFoundation/openvdb.git GIT_TAG aebaf8d95be5e57fd33949281ec357db4a576c2e #v6.2.1 - DEPENDS dep_blosc dep_openexr #dep_tbb dep_boost + DEPENDS dep_blosc dep_openexr dep_tbb dep_boost CMAKE_GENERATOR "${DEP_MSVC_GEN}" CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}" CMAKE_ARGS diff --git a/deps/openvdb-mods.patch b/deps/openvdb-mods.patch index 60687b8d1..023cb5308 100644 --- a/deps/openvdb-mods.patch +++ b/deps/openvdb-mods.patch @@ -1,8 +1,9 @@ -From e48f4a835fe7cb391f9f90945472bd367fb4c4f1 Mon Sep 17 00:00:00 2001 +From dbe038fce8a15ddc9a5c83ec5156d7bc9e178015 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 16 Oct 2019 17:42:50 +0200 Subject: [PATCH] Build fixes for PrusaSlicer integration +Signed-off-by: tamasmeszaros --- CMakeLists.txt | 3 - cmake/FindBlosc.cmake | 218 --------------- From 68a9980a5e7fb25a411c5b4deed2c09c4c6bd4c8 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 18 Nov 2019 10:17:44 +0100 Subject: [PATCH 53/67] ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE -> Small refactoring to improve performances --- src/libslic3r/Slicing.cpp | 16 +++++++++------- src/slic3r/GUI/GLCanvas3D.cpp | 2 ++ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/Slicing.cpp b/src/libslic3r/Slicing.cpp index 0d3b3af38..9fb2e00f4 100644 --- a/src/libslic3r/Slicing.cpp +++ b/src/libslic3r/Slicing.cpp @@ -361,16 +361,15 @@ std::vector smooth_height_profile(const std::vector& profile, co for (size_t i = 0; i < size; ++i) { - unsigned int id = 0; - double value = 0.0; - for (int j = (int)(i - radius); j <= (int)(i + radius); ++j) + ret.push_back(0.0); + double& height = ret.back(); + int begin = (int)(i - radius); + int end = (int)(i + radius); + for (int j = begin; j <= end; ++j) { if ((0 <= j) && (j < size)) - value += kernel[id] * profile[j]; - - ++id; + height += kernel[j - begin] * profile[j]; } - ret.push_back(value); } return ret; @@ -381,13 +380,16 @@ std::vector smooth_height_profile(const std::vector& profile, co std::vector heights; size_t heights_size = ret.size() / 2; heights.reserve(heights_size); + // extract heights from profile for (size_t i = 0; i < heights_size; ++i) { heights.push_back(ret[i * 2 + 1]); } + // smooth heights heights = gauss_blur(heights, std::max(radius, (unsigned int)1)); + // put smoothed heights back into profile for (size_t i = 0; i < heights_size; ++i) { ret[i * 2 + 1] = clamp(slicing_params.min_layer_height, slicing_params.max_layer_height, heights[i]); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index d0cd1fbeb..d9746bd05 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -287,6 +287,7 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING ImGui::Separator(); + imgui.disabled_begin(m_layer_height_profile.size() < 10); if (imgui.button(_(L("Smooth")))) wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), Event(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, m_smooth_radius)); @@ -299,6 +300,7 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const int radius = (int)m_smooth_radius; if (ImGui::SliderInt("##1", &radius, 1, 10)) m_smooth_radius = (unsigned int)radius; + imgui.disabled_end(); #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING ImGui::Separator(); From 28dedd65f0bd2149f8af9c3280be9b2fb2f54ac4 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 18 Nov 2019 10:25:23 +0100 Subject: [PATCH 54/67] ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE -> Code cleanup --- src/libslic3r/Slicing.cpp | 2 -- src/libslic3r/Slicing.hpp | 6 +++--- src/libslic3r/Technologies.hpp | 1 - src/slic3r/GUI/GLCanvas3D.cpp | 12 ------------ src/slic3r/GUI/GLCanvas3D.hpp | 8 -------- src/slic3r/GUI/Plater.cpp | 2 -- 6 files changed, 3 insertions(+), 28 deletions(-) diff --git a/src/libslic3r/Slicing.cpp b/src/libslic3r/Slicing.cpp index 9fb2e00f4..42e721744 100644 --- a/src/libslic3r/Slicing.cpp +++ b/src/libslic3r/Slicing.cpp @@ -330,7 +330,6 @@ std::vector layer_height_profile_adaptive( return layer_height_profile; } -#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING std::vector smooth_height_profile(const std::vector& profile, const SlicingParameters& slicing_params, unsigned int radius) { @@ -397,7 +396,6 @@ std::vector smooth_height_profile(const std::vector& profile, co return ret; } -#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING void adjust_layer_height_profile( const SlicingParameters &slicing_params, diff --git a/src/libslic3r/Slicing.hpp b/src/libslic3r/Slicing.hpp index 545857216..8dc0b47e9 100644 --- a/src/libslic3r/Slicing.hpp +++ b/src/libslic3r/Slicing.hpp @@ -147,10 +147,10 @@ extern std::vector layer_height_profile_adaptive( const SlicingParameters& slicing_params, const ModelObject& object, float cusp_value); -#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING -extern std::vector smooth_height_profile(const std::vector& profile, const SlicingParameters& slicing_params, +extern std::vector smooth_height_profile( + const std::vector& profile, + const SlicingParameters& slicing_params, unsigned int radius); -#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING #else extern std::vector layer_height_profile_adaptive( const SlicingParameters &slicing_params, diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 465f5dd33..f8db1b7b3 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -44,6 +44,5 @@ // Enable adaptive layer height profile #define ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE (1 && ENABLE_2_2_0_ALPHA1) -#define ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING (1 && ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE) #endif // _technologies_h_ diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index d9746bd05..5c5fce5c6 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -132,9 +132,7 @@ GLCanvas3D::LayersEditing::LayersEditing() , m_layer_height_profile_modified(false) #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE , m_adaptive_cusp(0.2f) -#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING , m_smooth_radius(5) -#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE , state(Unknown) , band_width(2.0f) @@ -285,7 +283,6 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const m_adaptive_cusp = std::min(m_adaptive_cusp, (float)m_slicing_parameters->max_layer_height); ImGui::SliderFloat("", &m_adaptive_cusp, 0.0f, (float)m_slicing_parameters->max_layer_height, "%.2f"); -#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING ImGui::Separator(); imgui.disabled_begin(m_layer_height_profile.size() < 10); if (imgui.button(_(L("Smooth")))) @@ -301,7 +298,6 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const if (ImGui::SliderInt("##1", &radius, 1, 10)) m_smooth_radius = (unsigned int)radius; imgui.disabled_end(); -#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING ImGui::Separator(); if (imgui.button(_(L("Reset")))) @@ -617,7 +613,6 @@ void GLCanvas3D::LayersEditing::adaptive_layer_height_profile(GLCanvas3D& canvas canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); } -#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING void GLCanvas3D::LayersEditing::smooth_layer_height_profile(GLCanvas3D& canvas, unsigned int radius) { m_layer_height_profile = smooth_height_profile(m_layer_height_profile, *m_slicing_parameters, radius); @@ -625,8 +620,6 @@ void GLCanvas3D::LayersEditing::smooth_layer_height_profile(GLCanvas3D& canvas, m_layers_texture.valid = false; canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); } -#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING - #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE void GLCanvas3D::LayersEditing::generate_layer_height_texture() @@ -1256,9 +1249,7 @@ wxDEFINE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent); #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE wxDEFINE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, Event); -#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING wxDEFINE_EVENT(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, Event); -#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE #if ENABLE_THUMBNAIL_GENERATOR @@ -1577,15 +1568,12 @@ void GLCanvas3D::adaptive_layer_height_profile(float cusp) m_dirty = true; } -#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING void GLCanvas3D::smooth_layer_height_profile(unsigned int radius) { m_layers_editing.smooth_layer_height_profile(*this, radius); m_layers_editing.state = LayersEditing::Completed; m_dirty = true; } -#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING - #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE bool GLCanvas3D::is_reload_delayed() const diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index d57a5c8d6..4a1859d54 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -107,9 +107,7 @@ wxDECLARE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent); #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE wxDECLARE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, Event); -#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING wxDECLARE_EVENT(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, Event); -#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE class GLCanvas3D @@ -184,9 +182,7 @@ private: #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE mutable float m_adaptive_cusp; -#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING mutable unsigned int m_smooth_radius; -#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE class LayersTexture @@ -237,9 +233,7 @@ private: void reset_layer_height_profile(GLCanvas3D& canvas); #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE void adaptive_layer_height_profile(GLCanvas3D& canvas, float cusp); -#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING void smooth_layer_height_profile(GLCanvas3D& canvas, unsigned int radius); -#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE static float get_cursor_z_relative(const GLCanvas3D& canvas); @@ -537,9 +531,7 @@ public: #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE void reset_layer_height_profile(); void adaptive_layer_height_profile(float cusp); -#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING void smooth_layer_height_profile(unsigned int radius); -#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE bool is_reload_delayed() const; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 2750af7df..faf7a3d69 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2090,9 +2090,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE view3D_canvas->Bind(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, [this](SimpleEvent&) { this->view3D->get_canvas3d()->reset_layer_height_profile(); }); view3D_canvas->Bind(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, [this](Event& evt) { this->view3D->get_canvas3d()->adaptive_layer_height_profile(evt.data); }); -#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING view3D_canvas->Bind(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, [this](Event& evt) { this->view3D->get_canvas3d()->smooth_layer_height_profile(evt.data); }); -#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE // 3DScene/Toolbar: From cfc927b3711701c68ad1e594e10db86e69f489ad Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 18 Nov 2019 10:59:58 +0100 Subject: [PATCH 55/67] Fix qhull download --- deps/deps-unix-common.cmake | 6 ++++-- deps/deps-windows.cmake | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/deps/deps-unix-common.cmake b/deps/deps-unix-common.cmake index 944d2c06d..d0305bd4d 100644 --- a/deps/deps-unix-common.cmake +++ b/deps/deps-unix-common.cmake @@ -55,8 +55,10 @@ find_package(Git REQUIRED) ExternalProject_Add(dep_qhull EXCLUDE_FROM_ALL 1 - URL "https://github.com/qhull/qhull/archive/v7.3.2.tar.gz" - URL_HASH SHA256=619c8a954880d545194bc03359404ef36a1abd2dde03678089459757fd790cb0 + #URL "https://github.com/qhull/qhull/archive/v7.3.2.tar.gz" + #URL_HASH SHA256=619c8a954880d545194bc03359404ef36a1abd2dde03678089459757fd790cb0 + GIT_REPOSITORY https://github.com/qhull/qhull.git + GIT_TAG v7.3.2 CMAKE_ARGS -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local diff --git a/deps/deps-windows.cmake b/deps/deps-windows.cmake index 1b978bdde..cca88c308 100644 --- a/deps/deps-windows.cmake +++ b/deps/deps-windows.cmake @@ -218,8 +218,10 @@ find_package(Git REQUIRED) ExternalProject_Add(dep_qhull EXCLUDE_FROM_ALL 1 - URL "https://github.com/qhull/qhull/archive/v7.3.2.tar.gz" - URL_HASH SHA256=619c8a954880d545194bc03359404ef36a1abd2dde03678089459757fd790cb0 + #URL "https://github.com/qhull/qhull/archive/v7.3.2.tar.gz" + #URL_HASH SHA256=619c8a954880d545194bc03359404ef36a1abd2dde03678089459757fd790cb0 + GIT_REPOSITORY https://github.com/qhull/qhull.git + GIT_TAG v7.3.2 CMAKE_GENERATOR "${DEP_MSVC_GEN}" CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local From 3b8ee784008df955c8fc0ea4be7b1e935c0d7ce7 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 18 Nov 2019 11:05:25 +0100 Subject: [PATCH 56/67] Use the upstream qhull with the macos patch instead of patching manually --- deps/deps-unix-common.cmake | 4 +--- deps/deps-windows.cmake | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/deps/deps-unix-common.cmake b/deps/deps-unix-common.cmake index d0305bd4d..b2d82c950 100644 --- a/deps/deps-unix-common.cmake +++ b/deps/deps-unix-common.cmake @@ -58,13 +58,11 @@ ExternalProject_Add(dep_qhull #URL "https://github.com/qhull/qhull/archive/v7.3.2.tar.gz" #URL_HASH SHA256=619c8a954880d545194bc03359404ef36a1abd2dde03678089459757fd790cb0 GIT_REPOSITORY https://github.com/qhull/qhull.git - GIT_TAG v7.3.2 + GIT_TAG 7afedcc73666e46a9f1d74632412ebecf53b1b30 # v7.3.2 plus the mac build patch CMAKE_ARGS -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local ${DEP_CMAKE_OPTS} - PATCH_COMMAND ${GIT_EXECUTABLE} checkout -f -- . && git clean -df && - ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/qhull-mods.patch ) ExternalProject_Add(dep_blosc diff --git a/deps/deps-windows.cmake b/deps/deps-windows.cmake index cca88c308..603f24931 100644 --- a/deps/deps-windows.cmake +++ b/deps/deps-windows.cmake @@ -221,7 +221,7 @@ ExternalProject_Add(dep_qhull #URL "https://github.com/qhull/qhull/archive/v7.3.2.tar.gz" #URL_HASH SHA256=619c8a954880d545194bc03359404ef36a1abd2dde03678089459757fd790cb0 GIT_REPOSITORY https://github.com/qhull/qhull.git - GIT_TAG v7.3.2 + GIT_TAG 7afedcc73666e46a9f1d74632412ebecf53b1b30 # v7.3.2 plus the mac build patch CMAKE_GENERATOR "${DEP_MSVC_GEN}" CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local From 54357f846f695d9879fb023e8cb8de7804ca0dca Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 18 Nov 2019 12:09:46 +0100 Subject: [PATCH 57/67] Change blosc patching "checkout -f" to "reset --hard" --- deps/deps-unix-common.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/deps-unix-common.cmake b/deps/deps-unix-common.cmake index b2d82c950..7491aafe1 100644 --- a/deps/deps-unix-common.cmake +++ b/deps/deps-unix-common.cmake @@ -80,7 +80,7 @@ ExternalProject_Add(dep_blosc -DBUILD_TESTS=OFF -DBUILD_BENCHMARKS=OFF -DPREFER_EXTERNAL_ZLIB=ON - PATCH_COMMAND ${GIT_EXECUTABLE} checkout -f -- . && git clean -df && + PATCH_COMMAND ${GIT_EXECUTABLE} reset --hard && git clean -df && ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/blosc-mods.patch ) From da44618074fdbe7771a234be29b23d462ec7e3c4 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 18 Nov 2019 14:32:41 +0100 Subject: [PATCH 58/67] ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE -> Fixed adaptive layer height profile and layer height profile smoothing calculations --- src/libslic3r/Slicing.cpp | 85 ++++++++++++++++++++++------------- src/slic3r/GUI/GLCanvas3D.cpp | 2 - 2 files changed, 54 insertions(+), 33 deletions(-) diff --git a/src/libslic3r/Slicing.cpp b/src/libslic3r/Slicing.cpp index 42e721744..66e339246 100644 --- a/src/libslic3r/Slicing.cpp +++ b/src/libslic3r/Slicing.cpp @@ -257,17 +257,22 @@ std::vector layer_height_profile_adaptive( #endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE std::vector layer_height_profile; - layer_height_profile.push_back(0.); + layer_height_profile.push_back(0.0); layer_height_profile.push_back(slicing_params.first_object_layer_height); if (slicing_params.first_object_layer_height_fixed()) { layer_height_profile.push_back(slicing_params.first_object_layer_height); layer_height_profile.push_back(slicing_params.first_object_layer_height); } double slice_z = slicing_params.first_object_layer_height; - double height = slicing_params.first_object_layer_height; int current_facet = 0; +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + while (slice_z <= slicing_params.object_print_z_height()) { + double height = 999.0; +#else + double height = slicing_params.first_object_layer_height; while ((slice_z - height) <= slicing_params.object_print_z_height()) { height = 999.0; +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE // Slic3r::debugf "\n Slice layer: %d\n", $id; // determine next layer height double cusp_height = as.cusp_height((float)slice_z, cusp_value, current_facet); @@ -317,15 +322,26 @@ std::vector layer_height_profile_adaptive( layer_height_profile.push_back(slice_z); layer_height_profile.push_back(height); slice_z += height; +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE layer_height_profile.push_back(slice_z); layer_height_profile.push_back(height); +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE } +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + double z_gap = slicing_params.object_print_z_height() - layer_height_profile[layer_height_profile.size() - 2]; + if (z_gap > 0.0) + { + layer_height_profile.push_back(slicing_params.object_print_z_height()); + layer_height_profile.push_back(clamp(slicing_params.min_layer_height, slicing_params.max_layer_height, z_gap)); + } +#else double last = std::max(slicing_params.first_object_layer_height, layer_height_profile[layer_height_profile.size() - 2]); layer_height_profile.push_back(last); layer_height_profile.push_back(slicing_params.first_object_layer_height); layer_height_profile.push_back(slicing_params.object_print_z_height()); layer_height_profile.push_back(slicing_params.first_object_layer_height); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE return layer_height_profile; } @@ -333,7 +349,7 @@ std::vector layer_height_profile_adaptive( std::vector smooth_height_profile(const std::vector& profile, const SlicingParameters& slicing_params, unsigned int radius) { - auto gauss_blur = [] (const std::vector& profile, unsigned int radius) -> std::vector { + auto gauss_blur = [&slicing_params](const std::vector& profile, unsigned int radius) -> std::vector { auto gauss_kernel = [] (unsigned int radius) -> std::vector { unsigned int size = 2 * radius + 1; std::vector ret; @@ -353,48 +369,55 @@ std::vector smooth_height_profile(const std::vector& profile, co return ret; }; + // skip first layer ? + size_t skip_count = slicing_params.first_object_layer_height_fixed() ? 4 : 0; + + // not enough data to smmoth + if ((int)profile.size() - (int)skip_count < 6) + return profile; + + std::vector kernel = gauss_kernel(radius); + int two_radius = 2 * (int)radius; + std::vector ret; size_t size = profile.size(); ret.reserve(size); - std::vector kernel = gauss_kernel(radius); - for (size_t i = 0; i < size; ++i) + // leave first layer untouched + for (size_t i = 0; i < skip_count; ++i) { + ret.push_back(profile[i]); + } + + // smooth the rest of the profile + double max_dz = (double)radius * slicing_params.layer_height; + for (size_t i = skip_count; i < size; i += 2) + { + double z = profile[i]; + ret.push_back(z); ret.push_back(0.0); double& height = ret.back(); - int begin = (int)(i - radius); - int end = (int)(i + radius); - for (int j = begin; j <= end; ++j) + int begin = std::max((int)i - two_radius, (int)skip_count); + int end = std::min((int)i + two_radius, (int)size - 2); + double kernel_total = 0.0; + for (int j = begin; j <= end; j += 2) { - if ((0 <= j) && (j < size)) - height += kernel[j - begin] * profile[j]; + int kernel_id = radius + (j - (int)i) / 2; + double dz = std::abs(z - profile[j]); + if (dz * slicing_params.layer_height <= max_dz) + { + height += kernel[kernel_id] * profile[j + 1]; + kernel_total += kernel[kernel_id]; + } } + + height = clamp(slicing_params.min_layer_height, slicing_params.max_layer_height, (kernel_total != 0.0) ? height /= kernel_total : profile[i + 1]); } return ret; }; - std::vector ret = profile; - - std::vector heights; - size_t heights_size = ret.size() / 2; - heights.reserve(heights_size); - // extract heights from profile - for (size_t i = 0; i < heights_size; ++i) - { - heights.push_back(ret[i * 2 + 1]); - } - - // smooth heights - heights = gauss_blur(heights, std::max(radius, (unsigned int)1)); - - // put smoothed heights back into profile - for (size_t i = 0; i < heights_size; ++i) - { - ret[i * 2 + 1] = clamp(slicing_params.min_layer_height, slicing_params.max_layer_height, heights[i]); - } - - return ret; + return gauss_blur(profile, std::max(radius, (unsigned int)1)); } void adjust_layer_height_profile( diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 5c5fce5c6..7827a9566 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -284,7 +284,6 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const ImGui::SliderFloat("", &m_adaptive_cusp, 0.0f, (float)m_slicing_parameters->max_layer_height, "%.2f"); ImGui::Separator(); - imgui.disabled_begin(m_layer_height_profile.size() < 10); if (imgui.button(_(L("Smooth")))) wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), Event(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, m_smooth_radius)); @@ -297,7 +296,6 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const int radius = (int)m_smooth_radius; if (ImGui::SliderInt("##1", &radius, 1, 10)) m_smooth_radius = (unsigned int)radius; - imgui.disabled_end(); ImGui::Separator(); if (imgui.button(_(L("Reset")))) From b8c09d7a34583343301c6fd697232d661399a128 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 18 Nov 2019 15:31:55 +0100 Subject: [PATCH 59/67] Content of file 90-3dconnexion.rules formatted as in: https://github.com/libusb/hidapi/blob/master/udev/99-hid.rules --- resources/udev/90-3dconnexion.rules | 124 +++++++++++++++++++--------- 1 file changed, 84 insertions(+), 40 deletions(-) diff --git a/resources/udev/90-3dconnexion.rules b/resources/udev/90-3dconnexion.rules index 670d91790..df010f105 100644 --- a/resources/udev/90-3dconnexion.rules +++ b/resources/udev/90-3dconnexion.rules @@ -1,45 +1,89 @@ # See src/slic3r/GUI/Mouse3DController.cpp for the list of devices # Logitech vendor devices -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c603", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c605", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c606", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c621", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c623", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c625", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c626", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c627", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c628", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c629", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c62b", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c62e", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c62f", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c631", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c632", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c633", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c635", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c636", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c640", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c652", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c603", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c605", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c606", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c621", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c623", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c625", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c626", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c627", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c628", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c629", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c62b", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c62e", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c62f", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c631", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c632", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c633", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c635", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c636", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c640", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c652", MODE="0666" # 3D Connexion vendor devices -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c603", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c605", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c606", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c621", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c623", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c625", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c626", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c627", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c628", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c629", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c62b", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c62e", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c62f", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c631", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c632", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c633", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c635", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c636", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c640", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c652", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c603", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c605", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c606", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c621", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c623", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c625", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c626", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c627", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c628", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c629", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c62b", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c62e", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c62f", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c631", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c632", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c633", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c635", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c636", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c640", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c652", MODE="0666" + +## Logitech vendor devices +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c603", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c605", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c606", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c621", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c623", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c625", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c626", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c627", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c628", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c629", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c62b", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c62e", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c62f", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c631", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c632", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c633", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c635", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c636", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c640", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c652", MODE="0666" +# +## 3D Connexion vendor devices +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c603", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c605", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c606", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c621", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c623", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c625", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c626", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c627", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c628", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c629", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c62b", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c62e", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c62f", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c631", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c632", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c633", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c635", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c636", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c640", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c652", MODE="0666" From 4fceff6ae37889aee40f6a21d8da8e8cdc318f05 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 18 Nov 2019 16:07:39 +0100 Subject: [PATCH 60/67] Cleanup of file 90-3dconnexion.rules --- resources/udev/90-3dconnexion.rules | 44 ----------------------------- 1 file changed, 44 deletions(-) diff --git a/resources/udev/90-3dconnexion.rules b/resources/udev/90-3dconnexion.rules index df010f105..04d581498 100644 --- a/resources/udev/90-3dconnexion.rules +++ b/resources/udev/90-3dconnexion.rules @@ -43,47 +43,3 @@ KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c635", MODE="0666 KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c636", MODE="0666" KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c640", MODE="0666" KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c652", MODE="0666" - -## Logitech vendor devices -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c603", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c605", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c606", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c621", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c623", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c625", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c626", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c627", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c628", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c629", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c62b", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c62e", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c62f", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c631", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c632", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c633", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c635", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c636", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c640", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c652", MODE="0666" -# -## 3D Connexion vendor devices -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c603", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c605", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c606", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c621", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c623", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c625", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c626", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c627", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c628", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c629", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c62b", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c62e", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c62f", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c631", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c632", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c633", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c635", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c636", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c640", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c652", MODE="0666" From 25ab9e8d5d2f9e061b453528a7cf80a0ac66e9d2 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 19 Nov 2019 11:43:20 +0100 Subject: [PATCH 61/67] Fixed normals transformation in functions stl_transform() --- src/admesh/stl.h | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/admesh/stl.h b/src/admesh/stl.h index fa0edec2b..9224b0459 100644 --- a/src/admesh/stl.h +++ b/src/admesh/stl.h @@ -184,10 +184,21 @@ extern void stl_mirror_xz(stl_file *stl); extern void stl_get_size(stl_file *stl); +// the following function is not used +/* template extern void stl_transform(stl_file *stl, T *trafo3x4) { - for (uint32_t i_face = 0; i_face < stl->stats.number_of_facets; ++ i_face) { + Eigen::Matrix trafo3x3; + for (int i = 0; i < 3; ++i) + { + for (int j = 0; j < 3; ++j) + { + trafo3x3(i, j) = (i * 4) + j; + } + } + Eigen::Matrix r = trafo3x3.inverse().transpose(); + for (uint32_t i_face = 0; i_face < stl->stats.number_of_facets; ++ i_face) { stl_facet &face = stl->facet_start[i_face]; for (int i_vertex = 0; i_vertex < 3; ++ i_vertex) { stl_vertex &v_dst = face.vertex[i_vertex]; @@ -196,21 +207,18 @@ extern void stl_transform(stl_file *stl, T *trafo3x4) v_dst(1) = T(trafo3x4[4] * v_src(0) + trafo3x4[5] * v_src(1) + trafo3x4[6] * v_src(2) + trafo3x4[7]); v_dst(2) = T(trafo3x4[8] * v_src(0) + trafo3x4[9] * v_src(1) + trafo3x4[10] * v_src(2) + trafo3x4[11]); } - stl_vertex &v_dst = face.normal; - stl_vertex v_src = v_dst; - v_dst(0) = T(trafo3x4[0] * v_src(0) + trafo3x4[1] * v_src(1) + trafo3x4[2] * v_src(2)); - v_dst(1) = T(trafo3x4[4] * v_src(0) + trafo3x4[5] * v_src(1) + trafo3x4[6] * v_src(2)); - v_dst(2) = T(trafo3x4[8] * v_src(0) + trafo3x4[9] * v_src(1) + trafo3x4[10] * v_src(2)); - } + face.normal = (r * face.normal.template cast()).template cast().eval(); + } stl_get_size(stl); } +*/ template inline void stl_transform(stl_file *stl, const Eigen::Transform& t) { - const Eigen::Matrix r = t.matrix().template block<3, 3>(0, 0); - for (size_t i = 0; i < stl->stats.number_of_facets; ++ i) { + const Eigen::Matrix r = t.matrix().template block<3, 3>(0, 0).inverse().transpose(); + for (size_t i = 0; i < stl->stats.number_of_facets; ++ i) { stl_facet &f = stl->facet_start[i]; for (size_t j = 0; j < 3; ++j) f.vertex[j] = (t * f.vertex[j].template cast()).template cast().eval(); @@ -223,12 +231,13 @@ inline void stl_transform(stl_file *stl, const Eigen::Transform inline void stl_transform(stl_file *stl, const Eigen::Matrix& m) { - for (size_t i = 0; i < stl->stats.number_of_facets; ++ i) { + const Eigen::Matrix r = m.inverse().transpose(); + for (size_t i = 0; i < stl->stats.number_of_facets; ++ i) { stl_facet &f = stl->facet_start[i]; for (size_t j = 0; j < 3; ++j) f.vertex[j] = (m * f.vertex[j].template cast()).template cast().eval(); - f.normal = (m * f.normal.template cast()).template cast().eval(); - } + f.normal = (r * f.normal.template cast()).template cast().eval(); + } stl_get_size(stl); } From 3b084c50cdb3c0f4e52b3a320374db2807a4929a Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 19 Nov 2019 14:44:42 +0100 Subject: [PATCH 62/67] Revert to older texture for mini printer printbed --- resources/icons/bed/mini.svg | 145 +++++++++++++---------------------- 1 file changed, 53 insertions(+), 92 deletions(-) diff --git a/resources/icons/bed/mini.svg b/resources/icons/bed/mini.svg index 1b9476ef8..93c3437bd 100644 --- a/resources/icons/bed/mini.svg +++ b/resources/icons/bed/mini.svg @@ -1,109 +1,70 @@ - - bed_texture_denser - - - - - - - - - - + + MINI_bed_texture + + + + + + + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 79d516ca7f9b14c3083c1048937448c4d76a926f Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 19 Nov 2019 14:58:27 +0100 Subject: [PATCH 63/67] ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE -> Smoothing algorithm modified to give more weight close to height limits --- src/libslic3r/Slicing.cpp | 27 ++++++++++++++++----------- src/libslic3r/SlicingAdaptive.cpp | 4 ++++ 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/libslic3r/Slicing.cpp b/src/libslic3r/Slicing.cpp index 66e339246..1badd9b14 100644 --- a/src/libslic3r/Slicing.cpp +++ b/src/libslic3r/Slicing.cpp @@ -346,8 +346,7 @@ std::vector layer_height_profile_adaptive( return layer_height_profile; } -std::vector smooth_height_profile(const std::vector& profile, const SlicingParameters& slicing_params, - unsigned int radius) +std::vector smooth_height_profile(const std::vector& profile, const SlicingParameters& slicing_params, unsigned int radius) { auto gauss_blur = [&slicing_params](const std::vector& profile, unsigned int radius) -> std::vector { auto gauss_kernel = [] (unsigned int radius) -> std::vector { @@ -390,28 +389,34 @@ std::vector smooth_height_profile(const std::vector& profile, co } // smooth the rest of the profile - double max_dz = (double)radius * slicing_params.layer_height; + double med_h = 0.5 * (slicing_params.min_layer_height + slicing_params.max_layer_height); + double half_delta_h = 0.5 * (slicing_params.max_layer_height - slicing_params.min_layer_height); + double inv_half_delta_h = (half_delta_h > 0.0) ? 1.0 / half_delta_h : 1.0; + + double max_dz_band = (double)radius * slicing_params.layer_height; for (size_t i = skip_count; i < size; i += 2) { - double z = profile[i]; - ret.push_back(z); + double zi = profile[i]; + ret.push_back(zi); ret.push_back(0.0); double& height = ret.back(); int begin = std::max((int)i - two_radius, (int)skip_count); int end = std::min((int)i + two_radius, (int)size - 2); - double kernel_total = 0.0; + double weight_total = 0.0; for (int j = begin; j <= end; j += 2) { int kernel_id = radius + (j - (int)i) / 2; - double dz = std::abs(z - profile[j]); - if (dz * slicing_params.layer_height <= max_dz) + double dz = std::abs(zi - profile[j]); + if (dz * slicing_params.layer_height <= max_dz_band) { - height += kernel[kernel_id] * profile[j + 1]; - kernel_total += kernel[kernel_id]; + double dh = std::abs(profile[j + 1] - med_h); + double weight = kernel[kernel_id] * dh * inv_half_delta_h; + height += weight * profile[j + 1]; + weight_total += weight; } } - height = clamp(slicing_params.min_layer_height, slicing_params.max_layer_height, (kernel_total != 0.0) ? height /= kernel_total : profile[i + 1]); + height = clamp(slicing_params.min_layer_height, slicing_params.max_layer_height, (weight_total != 0.0) ? height /= weight_total : profile[i + 1]); } return ret; diff --git a/src/libslic3r/SlicingAdaptive.cpp b/src/libslic3r/SlicingAdaptive.cpp index e64012553..bc02a89c1 100644 --- a/src/libslic3r/SlicingAdaptive.cpp +++ b/src/libslic3r/SlicingAdaptive.cpp @@ -37,6 +37,10 @@ void SlicingAdaptive::prepare() m_mesh = m_object->raw_mesh(); const ModelInstance* first_instance = m_object->instances.front(); m_mesh.transform(first_instance->get_matrix(), first_instance->is_left_handed()); + for (stl_facet& facet : m_mesh.stl.facet_start) + { + facet.normal.normalize(); + } // 1) Collect faces from mesh. m_faces.reserve(m_mesh.stl.stats.number_of_facets); From 4982e177a26ccfdd93b875da7bb6fa635c56fe3c Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 19 Nov 2019 15:39:49 +0100 Subject: [PATCH 64/67] ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE -> Attempt to fix imgui dialog position on Mac --- src/slic3r/GUI/GLCanvas3D.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 7827a9566..4721901f4 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -230,7 +230,11 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const float canvas_w = (float)cnv_size.get_width(); float canvas_h = (float)cnv_size.get_height(); +#if ENABLE_RETINA_GL + const float scale_gl = m_retina_helper->get_scale_factor(); +#else const float scale_gl = wxGetApp().mainframe->scale_factor(); +#endif // ENABLE_RETINA_GL ImGuiWrapper& imgui = *wxGetApp().imgui(); imgui.set_next_window_pos(canvas_w - scale_gl * THICKNESS_BAR_WIDTH, canvas_h, ImGuiCond_Always, 1.0f, 1.0f); From 955439b3baf8ed03fa541e11325ec98a2083e061 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 20 Nov 2019 10:57:56 +0100 Subject: [PATCH 65/67] Fixed build on Mac --- src/slic3r/GUI/GLCanvas3D.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 4721901f4..818c2ae63 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -230,14 +230,8 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const float canvas_w = (float)cnv_size.get_width(); float canvas_h = (float)cnv_size.get_height(); -#if ENABLE_RETINA_GL - const float scale_gl = m_retina_helper->get_scale_factor(); -#else - const float scale_gl = wxGetApp().mainframe->scale_factor(); -#endif // ENABLE_RETINA_GL - ImGuiWrapper& imgui = *wxGetApp().imgui(); - imgui.set_next_window_pos(canvas_w - scale_gl * THICKNESS_BAR_WIDTH, canvas_h, ImGuiCond_Always, 1.0f, 1.0f); + imgui.set_next_window_pos(canvas_w - imgui.get_style_scaling() * THICKNESS_BAR_WIDTH, canvas_h, ImGuiCond_Always, 1.0f, 1.0f); imgui.set_next_window_bg_alpha(0.5f); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); From aea32ffe7272186ca20e30c3edea8ff92c1048b7 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 20 Nov 2019 14:06:30 +0100 Subject: [PATCH 66/67] ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE -> Added 'keep min' option to smoothing algorithm --- src/libslic3r/Slicing.cpp | 26 +++++++++++++++----------- src/libslic3r/Slicing.hpp | 14 +++++++++++--- src/slic3r/GUI/GLCanvas3D.cpp | 24 +++++++++++++++--------- src/slic3r/GUI/GLCanvas3D.hpp | 10 ++++++---- src/slic3r/GUI/Plater.cpp | 2 +- 5 files changed, 48 insertions(+), 28 deletions(-) diff --git a/src/libslic3r/Slicing.cpp b/src/libslic3r/Slicing.cpp index 1badd9b14..81e9bf0c5 100644 --- a/src/libslic3r/Slicing.cpp +++ b/src/libslic3r/Slicing.cpp @@ -346,9 +346,9 @@ std::vector layer_height_profile_adaptive( return layer_height_profile; } -std::vector smooth_height_profile(const std::vector& profile, const SlicingParameters& slicing_params, unsigned int radius) +std::vector smooth_height_profile(const std::vector& profile, const SlicingParameters& slicing_params, const HeightProfileSmoothingParams& smoothing_params) { - auto gauss_blur = [&slicing_params](const std::vector& profile, unsigned int radius) -> std::vector { + auto gauss_blur = [&slicing_params](const std::vector& profile, const HeightProfileSmoothingParams& smoothing_params) -> std::vector { auto gauss_kernel = [] (unsigned int radius) -> std::vector { unsigned int size = 2 * radius + 1; std::vector ret; @@ -374,7 +374,8 @@ std::vector smooth_height_profile(const std::vector& profile, co // not enough data to smmoth if ((int)profile.size() - (int)skip_count < 6) return profile; - + + unsigned int radius = std::max(smoothing_params.radius, (unsigned int)1); std::vector kernel = gauss_kernel(radius); int two_radius = 2 * (int)radius; @@ -388,15 +389,16 @@ std::vector smooth_height_profile(const std::vector& profile, co ret.push_back(profile[i]); } - // smooth the rest of the profile - double med_h = 0.5 * (slicing_params.min_layer_height + slicing_params.max_layer_height); - double half_delta_h = 0.5 * (slicing_params.max_layer_height - slicing_params.min_layer_height); - double inv_half_delta_h = (half_delta_h > 0.0) ? 1.0 / half_delta_h : 1.0; + // smooth the rest of the profile by biasing a gaussian blur + // the bias moves the smoothed profile closer to the min_layer_height + double delta_h = slicing_params.max_layer_height - slicing_params.min_layer_height; + double inv_delta_h = (delta_h != 0.0) ? 1.0 / delta_h : 1.0; double max_dz_band = (double)radius * slicing_params.layer_height; for (size_t i = skip_count; i < size; i += 2) { double zi = profile[i]; + double hi = profile[i + 1]; ret.push_back(zi); ret.push_back(0.0); double& height = ret.back(); @@ -409,20 +411,22 @@ std::vector smooth_height_profile(const std::vector& profile, co double dz = std::abs(zi - profile[j]); if (dz * slicing_params.layer_height <= max_dz_band) { - double dh = std::abs(profile[j + 1] - med_h); - double weight = kernel[kernel_id] * dh * inv_half_delta_h; + double dh = std::abs(slicing_params.max_layer_height - profile[j + 1]); + double weight = kernel[kernel_id] * sqrt(dh * inv_delta_h); height += weight * profile[j + 1]; weight_total += weight; } } - height = clamp(slicing_params.min_layer_height, slicing_params.max_layer_height, (weight_total != 0.0) ? height /= weight_total : profile[i + 1]); + height = clamp(slicing_params.min_layer_height, slicing_params.max_layer_height, (weight_total != 0.0) ? height /= weight_total : hi); + if (smoothing_params.keep_min) + height = std::min(height, hi); } return ret; }; - return gauss_blur(profile, std::max(radius, (unsigned int)1)); + return gauss_blur(profile, smoothing_params); } void adjust_layer_height_profile( diff --git a/src/libslic3r/Slicing.hpp b/src/libslic3r/Slicing.hpp index 8dc0b47e9..03ef7e67d 100644 --- a/src/libslic3r/Slicing.hpp +++ b/src/libslic3r/Slicing.hpp @@ -147,10 +147,18 @@ extern std::vector layer_height_profile_adaptive( const SlicingParameters& slicing_params, const ModelObject& object, float cusp_value); +struct HeightProfileSmoothingParams +{ + unsigned int radius; + bool keep_min; + + HeightProfileSmoothingParams() : radius(5), keep_min(false) {} + HeightProfileSmoothingParams(unsigned int radius, bool keep_min) : radius(radius), keep_min(keep_min) {} +}; + extern std::vector smooth_height_profile( - const std::vector& profile, - const SlicingParameters& slicing_params, - unsigned int radius); + const std::vector& profile, const SlicingParameters& slicing_params, + const HeightProfileSmoothingParams& smoothing_params); #else extern std::vector layer_height_profile_adaptive( const SlicingParameters &slicing_params, diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 818c2ae63..a459a3ec8 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -132,7 +132,6 @@ GLCanvas3D::LayersEditing::LayersEditing() , m_layer_height_profile_modified(false) #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE , m_adaptive_cusp(0.2f) - , m_smooth_radius(5) #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE , state(Unknown) , band_width(2.0f) @@ -283,7 +282,7 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const ImGui::Separator(); if (imgui.button(_(L("Smooth")))) - wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), Event(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, m_smooth_radius)); + wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), HeightProfileSmoothEvent(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, m_smooth_params )); ImGui::SameLine(); ImGui::SetCursorPosX(text_align); @@ -291,9 +290,16 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const ImGui::SameLine(); ImGui::PushItemWidth(120.0f); ImGui::SetCursorPosX(widget_align); - int radius = (int)m_smooth_radius; + int radius = (int)m_smooth_params.radius; if (ImGui::SliderInt("##1", &radius, 1, 10)) - m_smooth_radius = (unsigned int)radius; + m_smooth_params.radius = (unsigned int)radius; + + ImGui::SetCursorPosX(text_align); + imgui.text(_(L("Keep min"))); + ImGui::SameLine(); + ImGui::PushItemWidth(120.0f); + ImGui::SetCursorPosX(widget_align); + imgui.checkbox("##2", m_smooth_params.keep_min); ImGui::Separator(); if (imgui.button(_(L("Reset")))) @@ -609,9 +615,9 @@ void GLCanvas3D::LayersEditing::adaptive_layer_height_profile(GLCanvas3D& canvas canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); } -void GLCanvas3D::LayersEditing::smooth_layer_height_profile(GLCanvas3D& canvas, unsigned int radius) +void GLCanvas3D::LayersEditing::smooth_layer_height_profile(GLCanvas3D& canvas, const HeightProfileSmoothingParams& smoothing_params) { - m_layer_height_profile = smooth_height_profile(m_layer_height_profile, *m_slicing_parameters, radius); + m_layer_height_profile = smooth_height_profile(m_layer_height_profile, *m_slicing_parameters, smoothing_params); const_cast(m_model_object)->layer_height_profile = m_layer_height_profile; m_layers_texture.valid = false; canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); @@ -1245,7 +1251,7 @@ wxDEFINE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent); #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE wxDEFINE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, Event); -wxDEFINE_EVENT(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, Event); +wxDEFINE_EVENT(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, HeightProfileSmoothEvent); #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE #if ENABLE_THUMBNAIL_GENERATOR @@ -1564,9 +1570,9 @@ void GLCanvas3D::adaptive_layer_height_profile(float cusp) m_dirty = true; } -void GLCanvas3D::smooth_layer_height_profile(unsigned int radius) +void GLCanvas3D::smooth_layer_height_profile(const HeightProfileSmoothingParams& smoothing_params) { - m_layers_editing.smooth_layer_height_profile(*this, radius); + m_layers_editing.smooth_layer_height_profile(*this, smoothing_params); m_layers_editing.state = LayersEditing::Completed; m_dirty = true; } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 4a1859d54..ad35f3909 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -81,6 +81,8 @@ template using Vec2dsEvent = ArrayEvent; using Vec3dEvent = Event; template using Vec3dsEvent = ArrayEvent; +using HeightProfileSmoothEvent = Event; + wxDECLARE_EVENT(EVT_GLCANVAS_INIT, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, RBtnEvent); @@ -107,7 +109,7 @@ wxDECLARE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent); #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE wxDECLARE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, Event); -wxDECLARE_EVENT(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, Event); +wxDECLARE_EVENT(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, HeightProfileSmoothEvent); #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE class GLCanvas3D @@ -182,7 +184,7 @@ private: #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE mutable float m_adaptive_cusp; - mutable unsigned int m_smooth_radius; + mutable HeightProfileSmoothingParams m_smooth_params; #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE class LayersTexture @@ -233,7 +235,7 @@ private: void reset_layer_height_profile(GLCanvas3D& canvas); #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE void adaptive_layer_height_profile(GLCanvas3D& canvas, float cusp); - void smooth_layer_height_profile(GLCanvas3D& canvas, unsigned int radius); + void smooth_layer_height_profile(GLCanvas3D& canvas, const HeightProfileSmoothingParams& smoothing_paramsn); #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE static float get_cursor_z_relative(const GLCanvas3D& canvas); @@ -531,7 +533,7 @@ public: #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE void reset_layer_height_profile(); void adaptive_layer_height_profile(float cusp); - void smooth_layer_height_profile(unsigned int radius); + void smooth_layer_height_profile(const HeightProfileSmoothingParams& smoothing_params); #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE bool is_reload_delayed() const; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index faf7a3d69..563bb5133 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2090,7 +2090,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE view3D_canvas->Bind(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, [this](SimpleEvent&) { this->view3D->get_canvas3d()->reset_layer_height_profile(); }); view3D_canvas->Bind(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, [this](Event& evt) { this->view3D->get_canvas3d()->adaptive_layer_height_profile(evt.data); }); - view3D_canvas->Bind(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, [this](Event& evt) { this->view3D->get_canvas3d()->smooth_layer_height_profile(evt.data); }); + view3D_canvas->Bind(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, [this](HeightProfileSmoothEvent& evt) { this->view3D->get_canvas3d()->smooth_layer_height_profile(evt.data); }); #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE // 3DScene/Toolbar: From 6df506e4824ee26b68379cb4760c1e22a509385e Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 22 Nov 2019 12:39:03 +0100 Subject: [PATCH 67/67] ENABLE_THUMBNAIL_GENERATOR -> Thumbnails generated using a callback function --- src/libslic3r/GCode.cpp | 113 ++++++++++---------- src/libslic3r/GCode.hpp | 10 +- src/libslic3r/GCode/ThumbnailData.hpp | 4 + src/libslic3r/Print.cpp | 4 +- src/libslic3r/Print.hpp | 8 +- src/slic3r/GUI/BackgroundSlicingProcess.cpp | 19 ++-- src/slic3r/GUI/BackgroundSlicingProcess.hpp | 9 +- src/slic3r/GUI/Plater.cpp | 77 ++++--------- 8 files changed, 108 insertions(+), 136 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 56a94b28c..28bcdf50c 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -6,9 +6,6 @@ #include "Geometry.hpp" #include "GCode/PrintExtents.hpp" #include "GCode/WipeTower.hpp" -#if ENABLE_THUMBNAIL_GENERATOR -#include "GCode/ThumbnailData.hpp" -#endif // ENABLE_THUMBNAIL_GENERATOR #include "ShortestPath.hpp" #include "Utils.hpp" @@ -695,7 +692,7 @@ std::vector>> GCode::collec } #if ENABLE_THUMBNAIL_GENERATOR -void GCode::do_export(Print* print, const char* path, GCodePreviewData* preview_data, const std::vector* thumbnail_data) +void GCode::do_export(Print* print, const char* path, GCodePreviewData* preview_data, ThumbnailsGeneratorCallback thumbnail_cb) #else void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_data) #endif // ENABLE_THUMBNAIL_GENERATOR @@ -725,7 +722,7 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ try { m_placeholder_parser_failed_templates.clear(); #if ENABLE_THUMBNAIL_GENERATOR - this->_do_export(*print, file, thumbnail_data); + this->_do_export(*print, file, thumbnail_cb); #else this->_do_export(*print, file); #endif // ENABLE_THUMBNAIL_GENERATOR @@ -793,9 +790,9 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ } #if ENABLE_THUMBNAIL_GENERATOR -void GCode::_do_export(Print& print, FILE* file, const std::vector* thumbnail_data) +void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thumbnail_cb) #else -void GCode::_do_export(Print &print, FILE *file) +void GCode::_do_export(Print& print, FILE* file) #endif // ENABLE_THUMBNAIL_GENERATOR { PROFILE_FUNC(); @@ -812,46 +809,46 @@ void GCode::_do_export(Print &print, FILE *file) // shall be adjusted as well to produce a G-code block compatible with the particular firmware flavor. if (print.config().gcode_flavor.value == gcfMarlin) { m_normal_time_estimator.set_max_acceleration((float)print.config().machine_max_acceleration_extruding.values[0]); - m_normal_time_estimator.set_retract_acceleration((float)print.config().machine_max_acceleration_retracting.values[0]); - m_normal_time_estimator.set_minimum_feedrate((float)print.config().machine_min_extruding_rate.values[0]); - m_normal_time_estimator.set_minimum_travel_feedrate((float)print.config().machine_min_travel_rate.values[0]); - m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, (float)print.config().machine_max_acceleration_x.values[0]); - m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, (float)print.config().machine_max_acceleration_y.values[0]); - m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, (float)print.config().machine_max_acceleration_z.values[0]); - m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, (float)print.config().machine_max_acceleration_e.values[0]); - m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, (float)print.config().machine_max_feedrate_x.values[0]); - m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, (float)print.config().machine_max_feedrate_y.values[0]); - m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, (float)print.config().machine_max_feedrate_z.values[0]); - m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, (float)print.config().machine_max_feedrate_e.values[0]); - m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, (float)print.config().machine_max_jerk_x.values[0]); - m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, (float)print.config().machine_max_jerk_y.values[0]); - m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, (float)print.config().machine_max_jerk_z.values[0]); - m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, (float)print.config().machine_max_jerk_e.values[0]); + m_normal_time_estimator.set_retract_acceleration((float)print.config().machine_max_acceleration_retracting.values[0]); + m_normal_time_estimator.set_minimum_feedrate((float)print.config().machine_min_extruding_rate.values[0]); + m_normal_time_estimator.set_minimum_travel_feedrate((float)print.config().machine_min_travel_rate.values[0]); + m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, (float)print.config().machine_max_acceleration_x.values[0]); + m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, (float)print.config().machine_max_acceleration_y.values[0]); + m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, (float)print.config().machine_max_acceleration_z.values[0]); + m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, (float)print.config().machine_max_acceleration_e.values[0]); + m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, (float)print.config().machine_max_feedrate_x.values[0]); + m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, (float)print.config().machine_max_feedrate_y.values[0]); + m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, (float)print.config().machine_max_feedrate_z.values[0]); + m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, (float)print.config().machine_max_feedrate_e.values[0]); + m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, (float)print.config().machine_max_jerk_x.values[0]); + m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, (float)print.config().machine_max_jerk_y.values[0]); + m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, (float)print.config().machine_max_jerk_z.values[0]); + m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, (float)print.config().machine_max_jerk_e.values[0]); if (m_silent_time_estimator_enabled) { m_silent_time_estimator.reset(); m_silent_time_estimator.set_dialect(print.config().gcode_flavor); - /* "Stealth mode" values can be just a copy of "normal mode" values + /* "Stealth mode" values can be just a copy of "normal mode" values * (when they aren't input for a printer preset). * Thus, use back value from values, instead of second one, which could be absent */ - m_silent_time_estimator.set_max_acceleration((float)print.config().machine_max_acceleration_extruding.values.back()); - m_silent_time_estimator.set_retract_acceleration((float)print.config().machine_max_acceleration_retracting.values.back()); - m_silent_time_estimator.set_minimum_feedrate((float)print.config().machine_min_extruding_rate.values.back()); - m_silent_time_estimator.set_minimum_travel_feedrate((float)print.config().machine_min_travel_rate.values.back()); - m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, (float)print.config().machine_max_acceleration_x.values.back()); - m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, (float)print.config().machine_max_acceleration_y.values.back()); - m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, (float)print.config().machine_max_acceleration_z.values.back()); - m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, (float)print.config().machine_max_acceleration_e.values.back()); - m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, (float)print.config().machine_max_feedrate_x.values.back()); - m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, (float)print.config().machine_max_feedrate_y.values.back()); - m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, (float)print.config().machine_max_feedrate_z.values.back()); - m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, (float)print.config().machine_max_feedrate_e.values.back()); - m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, (float)print.config().machine_max_jerk_x.values.back()); - m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, (float)print.config().machine_max_jerk_y.values.back()); - m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, (float)print.config().machine_max_jerk_z.values.back()); - m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, (float)print.config().machine_max_jerk_e.values.back()); + m_silent_time_estimator.set_max_acceleration((float)print.config().machine_max_acceleration_extruding.values.back()); + m_silent_time_estimator.set_retract_acceleration((float)print.config().machine_max_acceleration_retracting.values.back()); + m_silent_time_estimator.set_minimum_feedrate((float)print.config().machine_min_extruding_rate.values.back()); + m_silent_time_estimator.set_minimum_travel_feedrate((float)print.config().machine_min_travel_rate.values.back()); + m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, (float)print.config().machine_max_acceleration_x.values.back()); + m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, (float)print.config().machine_max_acceleration_y.values.back()); + m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, (float)print.config().machine_max_acceleration_z.values.back()); + m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, (float)print.config().machine_max_acceleration_e.values.back()); + m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, (float)print.config().machine_max_feedrate_x.values.back()); + m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, (float)print.config().machine_max_feedrate_y.values.back()); + m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, (float)print.config().machine_max_feedrate_z.values.back()); + m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, (float)print.config().machine_max_feedrate_e.values.back()); + m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, (float)print.config().machine_max_jerk_x.values.back()); + m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, (float)print.config().machine_max_jerk_y.values.back()); + m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, (float)print.config().machine_max_jerk_z.values.back()); + m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, (float)print.config().machine_max_jerk_e.values.back()); if (print.config().single_extruder_multi_material) { // As of now the fields are shown at the UI dialog in the same combo box as the ramming values, so they // are considered to be active for the single extruder multi-material printers only. @@ -909,7 +906,8 @@ void GCode::_do_export(Print &print, FILE *file) std::sort(zs.begin(), zs.end()); m_layer_count += (unsigned int)(object->copies().size() * (std::unique(zs.begin(), zs.end()) - zs.begin())); } - } else { + } + else { // Print all objects with the same print_z together. std::vector zs; for (auto object : print.objects()) { @@ -927,7 +925,7 @@ void GCode::_do_export(Print &print, FILE *file) m_enable_cooling_markers = true; this->apply_print_config(print.config()); this->set_extruders(print.extruders()); - + // Initialize colorprint. m_colorprint_heights = cast(print.config().colorprint_heights.values); @@ -936,31 +934,31 @@ void GCode::_do_export(Print &print, FILE *file) // get the minimum cross-section used in the print std::vector mm3_per_mm; for (auto object : print.objects()) { - for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) { + for (size_t region_id = 0; region_id < object->region_volumes.size(); ++region_id) { const PrintRegion* region = print.regions()[region_id]; for (auto layer : object->layers()) { const LayerRegion* layerm = layer->regions()[region_id]; - if (region->config().get_abs_value("perimeter_speed" ) == 0 || - region->config().get_abs_value("small_perimeter_speed" ) == 0 || - region->config().get_abs_value("external_perimeter_speed" ) == 0 || - region->config().get_abs_value("bridge_speed" ) == 0) + if (region->config().get_abs_value("perimeter_speed") == 0 || + region->config().get_abs_value("small_perimeter_speed") == 0 || + region->config().get_abs_value("external_perimeter_speed") == 0 || + region->config().get_abs_value("bridge_speed") == 0) mm3_per_mm.push_back(layerm->perimeters.min_mm3_per_mm()); - if (region->config().get_abs_value("infill_speed" ) == 0 || - region->config().get_abs_value("solid_infill_speed" ) == 0 || - region->config().get_abs_value("top_solid_infill_speed" ) == 0 || - region->config().get_abs_value("bridge_speed" ) == 0) + if (region->config().get_abs_value("infill_speed") == 0 || + region->config().get_abs_value("solid_infill_speed") == 0 || + region->config().get_abs_value("top_solid_infill_speed") == 0 || + region->config().get_abs_value("bridge_speed") == 0) mm3_per_mm.push_back(layerm->fills.min_mm3_per_mm()); } } - if (object->config().get_abs_value("support_material_speed" ) == 0 || - object->config().get_abs_value("support_material_interface_speed" ) == 0) + if (object->config().get_abs_value("support_material_speed") == 0 || + object->config().get_abs_value("support_material_interface_speed") == 0) for (auto layer : object->support_layers()) mm3_per_mm.push_back(layer->support_fills.min_mm3_per_mm()); } print.throw_if_canceled(); // filter out 0-width segments mm3_per_mm.erase(std::remove_if(mm3_per_mm.begin(), mm3_per_mm.end(), [](double v) { return v < 0.000001; }), mm3_per_mm.end()); - if (! mm3_per_mm.empty()) { + if (!mm3_per_mm.empty()) { // In order to honor max_print_speed we need to find a target volumetric // speed that we can use throughout the print. So we define this target // volumetric speed as the volumetric speed produced by printing the @@ -973,7 +971,7 @@ void GCode::_do_export(Print &print, FILE *file) } } print.throw_if_canceled(); - + m_cooling_buffer = make_unique(*this); if (print.config().spiral_vase.value) m_spiral_vase = make_unique(print.config()); @@ -991,11 +989,12 @@ void GCode::_do_export(Print &print, FILE *file) #if ENABLE_THUMBNAIL_GENERATOR // Write thumbnails using base64 encoding - if (thumbnail_data != nullptr) + if (thumbnail_cb != nullptr) { const size_t max_row_length = 78; - - for (const ThumbnailData& data : *thumbnail_data) + ThumbnailsList thumbnails; + thumbnail_cb(thumbnails, print.full_print_config().option("thumbnails")->values, true, true, false); + for (const ThumbnailData& data : thumbnails) { if (data.is_valid()) { diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 19ec5be3c..40794986a 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -17,6 +17,9 @@ #include "GCodeTimeEstimator.hpp" #include "EdgeGrid.hpp" #include "GCode/Analyzer.hpp" +#if ENABLE_THUMBNAIL_GENERATOR +#include "GCode/ThumbnailData.hpp" +#endif // ENABLE_THUMBNAIL_GENERATOR #include #include @@ -30,9 +33,6 @@ namespace Slic3r { // Forward declarations. class GCode; class GCodePreviewData; -#if ENABLE_THUMBNAIL_GENERATOR -struct ThumbnailData; -#endif // ENABLE_THUMBNAIL_GENERATOR class AvoidCrossingPerimeters { public: @@ -167,7 +167,7 @@ public: // throws std::runtime_exception on error, // throws CanceledException through print->throw_if_canceled(). #if ENABLE_THUMBNAIL_GENERATOR - void do_export(Print* print, const char* path, GCodePreviewData* preview_data = nullptr, const std::vector* thumbnail_data = nullptr); + void do_export(Print* print, const char* path, GCodePreviewData* preview_data = nullptr, ThumbnailsGeneratorCallback thumbnail_cb = nullptr); #else void do_export(Print *print, const char *path, GCodePreviewData *preview_data = nullptr); #endif // ENABLE_THUMBNAIL_GENERATOR @@ -199,7 +199,7 @@ public: protected: #if ENABLE_THUMBNAIL_GENERATOR - void _do_export(Print& print, FILE* file, const std::vector* thumbnail_data); + void _do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thumbnail_cb); #else void _do_export(Print &print, FILE *file); #endif //ENABLE_THUMBNAIL_GENERATOR diff --git a/src/libslic3r/GCode/ThumbnailData.hpp b/src/libslic3r/GCode/ThumbnailData.hpp index 9823ffd31..efe875e15 100644 --- a/src/libslic3r/GCode/ThumbnailData.hpp +++ b/src/libslic3r/GCode/ThumbnailData.hpp @@ -4,6 +4,7 @@ #if ENABLE_THUMBNAIL_GENERATOR #include +#include "libslic3r/Point.hpp" namespace Slic3r { @@ -20,6 +21,9 @@ struct ThumbnailData bool is_valid() const; }; +typedef std::vector ThumbnailsList; +typedef std::function ThumbnailsGeneratorCallback; + } // namespace Slic3r #endif // ENABLE_THUMBNAIL_GENERATOR diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 78a09c511..8b79f5367 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1538,7 +1538,7 @@ void Print::process() // write error into the G-code, cannot execute post-processing scripts). // It is up to the caller to show an error message. #if ENABLE_THUMBNAIL_GENERATOR -std::string Print::export_gcode(const std::string& path_template, GCodePreviewData* preview_data, const std::vector* thumbnail_data) +std::string Print::export_gcode(const std::string& path_template, GCodePreviewData* preview_data, ThumbnailsGeneratorCallback thumbnail_cb) #else std::string Print::export_gcode(const std::string &path_template, GCodePreviewData *preview_data) #endif // ENABLE_THUMBNAIL_GENERATOR @@ -1559,7 +1559,7 @@ std::string Print::export_gcode(const std::string &path_template, GCodePreviewDa // The following line may die for multiple reasons. GCode gcode; #if ENABLE_THUMBNAIL_GENERATOR - gcode.do_export(this, path.c_str(), preview_data, thumbnail_data); + gcode.do_export(this, path.c_str(), preview_data, thumbnail_cb); #else gcode.do_export(this, path.c_str(), preview_data); #endif // ENABLE_THUMBNAIL_GENERATOR diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 4fcd67166..1649f3c04 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -11,6 +11,9 @@ #include "Slicing.hpp" #include "GCode/ToolOrdering.hpp" #include "GCode/WipeTower.hpp" +#if ENABLE_THUMBNAIL_GENERATOR +#include "GCode/ThumbnailData.hpp" +#endif // ENABLE_THUMBNAIL_GENERATOR namespace Slic3r { @@ -19,9 +22,6 @@ class PrintObject; class ModelObject; class GCode; class GCodePreviewData; -#if ENABLE_THUMBNAIL_GENERATOR -struct ThumbnailData; -#endif // ENABLE_THUMBNAIL_GENERATOR // Print step IDs for keeping track of the print state. enum PrintStep { @@ -311,7 +311,7 @@ public: // Exports G-code into a file name based on the path_template, returns the file path of the generated G-code file. // If preview_data is not null, the preview_data is filled in for the G-code visualization (not used by the command line Slic3r). #if ENABLE_THUMBNAIL_GENERATOR - std::string export_gcode(const std::string& path_template, GCodePreviewData* preview_data, const std::vector* thumbnail_data = nullptr); + std::string export_gcode(const std::string& path_template, GCodePreviewData* preview_data, ThumbnailsGeneratorCallback thumbnail_cb = nullptr); #else std::string export_gcode(const std::string &path_template, GCodePreviewData *preview_data); #endif // ENABLE_THUMBNAIL_GENERATOR diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 5ab65f340..570841a45 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -20,9 +20,6 @@ #include "libslic3r/Utils.hpp" #include "libslic3r/GCode/PostProcessor.hpp" #include "libslic3r/GCode/PreviewData.hpp" -#if ENABLE_THUMBNAIL_GENERATOR -#include "libslic3r/GCode/ThumbnailData.hpp" -#endif // ENABLE_THUMBNAIL_GENERATOR #include "libslic3r/libslic3r.h" #include @@ -91,7 +88,7 @@ void BackgroundSlicingProcess::process_fff() m_print->process(); wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new wxCommandEvent(m_event_slicing_completed_id)); #if ENABLE_THUMBNAIL_GENERATOR - m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data, m_thumbnail_data); + m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data, m_thumbnail_cb); #else m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data); #endif // ENABLE_THUMBNAIL_GENERATOR @@ -139,9 +136,12 @@ void BackgroundSlicingProcess::process_sla() m_sla_print->export_raster(zipper); #if ENABLE_THUMBNAIL_GENERATOR - if (m_thumbnail_data != nullptr) + if (m_thumbnail_cb != nullptr) { - for (const ThumbnailData& data : *m_thumbnail_data) + ThumbnailsList thumbnails; + m_thumbnail_cb(thumbnails, current_print()->full_print_config().option("thumbnails")->values, true, true, false); +// m_thumbnail_cb(thumbnails, current_print()->full_print_config().option("thumbnails")->values, true, false, false); // renders also supports and pad + for (const ThumbnailData& data : thumbnails) { if (data.is_valid()) write_thumbnail(zipper, data); @@ -461,9 +461,12 @@ void BackgroundSlicingProcess::prepare_upload() Zipper zipper{source_path.string()}; m_sla_print->export_raster(zipper, m_upload_job.upload_data.upload_path.string()); #if ENABLE_THUMBNAIL_GENERATOR - if (m_thumbnail_data != nullptr) + if (m_thumbnail_cb != nullptr) { - for (const ThumbnailData& data : *m_thumbnail_data) + ThumbnailsList thumbnails; + m_thumbnail_cb(thumbnails, current_print()->full_print_config().option("thumbnails")->values, true, true, false); +// m_thumbnail_cb(thumbnails, current_print()->full_print_config().option("thumbnails")->values, true, false, false); // renders also supports and pad + for (const ThumbnailData& data : thumbnails) { if (data.is_valid()) write_thumbnail(zipper, data); diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/src/slic3r/GUI/BackgroundSlicingProcess.hpp index a603d52ac..984686e35 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.hpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.hpp @@ -17,9 +17,6 @@ namespace Slic3r { class DynamicPrintConfig; class GCodePreviewData; -#if ENABLE_THUMBNAIL_GENERATOR -struct ThumbnailData; -#endif // ENABLE_THUMBNAIL_GENERATOR class Model; class SLAPrint; @@ -53,7 +50,7 @@ public: void set_sla_print(SLAPrint *print) { m_sla_print = print; } void set_gcode_preview_data(GCodePreviewData *gpd) { m_gcode_preview_data = gpd; } #if ENABLE_THUMBNAIL_GENERATOR - void set_thumbnail_data(const std::vector* data) { m_thumbnail_data = data; } + void set_thumbnail_cb(ThumbnailsGeneratorCallback cb) { m_thumbnail_cb = cb; } #endif // ENABLE_THUMBNAIL_GENERATOR // The following wxCommandEvent will be sent to the UI thread / Platter window, when the slicing is finished @@ -159,8 +156,8 @@ private: // Data structure, to which the G-code export writes its annotations. GCodePreviewData *m_gcode_preview_data = nullptr; #if ENABLE_THUMBNAIL_GENERATOR - // Data structures, used to write thumbnails into gcode. - const std::vector* m_thumbnail_data = nullptr; + // Callback function, used to write thumbnails into gcode. + ThumbnailsGeneratorCallback m_thumbnail_cb = nullptr; #endif // ENABLE_THUMBNAIL_GENERATOR // Temporary G-code, there is one defined for the BackgroundSlicingProcess, differentiated from the other processes by a process ID. std::string m_temp_output_path; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index f04e1e17f..6f1c776fd 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1387,9 +1387,6 @@ struct Plater::priv Slic3r::Model model; PrinterTechnology printer_technology = ptFFF; Slic3r::GCodePreviewData gcode_preview_data; -#if ENABLE_THUMBNAIL_GENERATOR - std::vector thumbnail_data; -#endif // ENABLE_THUMBNAIL_GENERATOR // GUI elements wxSizer* panel_sizer{ nullptr }; @@ -1946,6 +1943,7 @@ struct Plater::priv #if ENABLE_THUMBNAIL_GENERATOR void generate_thumbnail(ThumbnailData& data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool transparent_background); + void generate_thumbnails(ThumbnailsList& thumbnails, const Vec2ds& sizes, bool printable_only, bool parts_only, bool transparent_background); #endif // ENABLE_THUMBNAIL_GENERATOR void msw_rescale_object_menu(); @@ -2016,7 +2014,15 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) background_process.set_sla_print(&sla_print); background_process.set_gcode_preview_data(&gcode_preview_data); #if ENABLE_THUMBNAIL_GENERATOR - background_process.set_thumbnail_data(&thumbnail_data); + background_process.set_thumbnail_cb([this](ThumbnailsList& thumbnails, const Vec2ds& sizes, bool printable_only, bool parts_only, bool transparent_background) + { + std::packaged_task task([this](ThumbnailsList& thumbnails, const Vec2ds& sizes, bool printable_only, bool parts_only, bool transparent_background) { + generate_thumbnails(thumbnails, sizes, printable_only, parts_only, transparent_background); + }); + std::future result = task.get_future(); + wxTheApp->CallAfter([&]() { task(thumbnails, sizes, printable_only, parts_only, transparent_background); }); + result.wait(); + }); #endif // ENABLE_THUMBNAIL_GENERATOR background_process.set_slicing_completed_event(EVT_SLICING_COMPLETED); background_process.set_finished_event(EVT_PROCESS_COMPLETED); @@ -3062,37 +3068,6 @@ bool Plater::priv::restart_background_process(unsigned int state) ( ((state & UPDATE_BACKGROUND_PROCESS_FORCE_RESTART) != 0 && ! this->background_process.finished()) || (state & UPDATE_BACKGROUND_PROCESS_FORCE_EXPORT) != 0 || (state & UPDATE_BACKGROUND_PROCESS_RESTART) != 0 ) ) { -#if ENABLE_THUMBNAIL_GENERATOR - if (((state & UPDATE_BACKGROUND_PROCESS_FORCE_EXPORT) == 0) && - (this->background_process.state() != BackgroundSlicingProcess::STATE_RUNNING)) - { - // update thumbnail data - const std::vector &thumbnail_sizes = this->background_process.current_print()->full_print_config().option("thumbnails")->values; - if (this->printer_technology == ptFFF) - { - // for ptFFF we need to generate the thumbnails before the export of gcode starts - this->thumbnail_data.clear(); - for (const Vec2d &sized : thumbnail_sizes) - { - this->thumbnail_data.push_back(ThumbnailData()); - Point size(sized); // round to ints - generate_thumbnail(this->thumbnail_data.back(), size.x(), size.y(), true, true, false); - } - } - else if (this->printer_technology == ptSLA) - { - // for ptSLA generate thumbnails without supports and pad (not yet calculated) - // to render also supports and pad see on_slicing_update() - this->thumbnail_data.clear(); - for (const Vec2d &sized : thumbnail_sizes) - { - this->thumbnail_data.push_back(ThumbnailData()); - Point size(sized); // round to ints - generate_thumbnail(this->thumbnail_data.back(), size.x(), size.y(), true, true, false); - } - } - } -#endif // ENABLE_THUMBNAIL_GENERATOR // The print is valid and it can be started. if (this->background_process.start()) { this->statusbar()->set_cancel_callback([this]() { @@ -3430,25 +3405,6 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt) } else if (evt.status.flags & PrintBase::SlicingStatus::RELOAD_SLA_PREVIEW) { // Update the SLA preview. Only called if not RELOAD_SLA_SUPPORT_POINTS, as the block above will refresh the preview anyways. this->preview->reload_print(); - - // uncomment the following lines if you want to render into the thumbnail also supports and pad for SLA printer -/* -#if ENABLE_THUMBNAIL_GENERATOR - // update thumbnail data - // for ptSLA generate the thumbnail after supports and pad have been calculated to have them rendered - if ((this->printer_technology == ptSLA) && (evt.status.percent == -3)) - { - const std::vector& thumbnail_sizes = this->background_process.current_print()->full_print_config().option("thumbnails")->values; - this->thumbnail_data.clear(); - for (const Vec2d &sized : thumbnail_sizes) - { - this->thumbnail_data.push_back(ThumbnailData()); - Point size(sized); // round to ints - generate_thumbnail(this->thumbnail_data.back(), size.x(), size.y(), true, false, false); - } - } -#endif // ENABLE_THUMBNAIL_GENERATOR -*/ } } @@ -3679,6 +3635,19 @@ void Plater::priv::generate_thumbnail(ThumbnailData& data, unsigned int w, unsig { view3D->get_canvas3d()->render_thumbnail(data, w, h, printable_only, parts_only, transparent_background); } + +void Plater::priv::generate_thumbnails(ThumbnailsList& thumbnails, const Vec2ds& sizes, bool printable_only, bool parts_only, bool transparent_background) +{ + thumbnails.clear(); + for (const Vec2d& size : sizes) + { + thumbnails.push_back(ThumbnailData()); + Point isize(size); // round to ints + generate_thumbnail(thumbnails.back(), isize.x(), isize.y(), printable_only, parts_only, transparent_background); + if (!thumbnails.back().is_valid()) + thumbnails.pop_back(); + } +} #endif // ENABLE_THUMBNAIL_GENERATOR void Plater::priv::msw_rescale_object_menu()