diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index 8b07562ba..77747cd07 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -220,6 +220,8 @@ add_library(libslic3r_gui STATIC ${LIBDIR}/slic3r/Config/Version.hpp ${LIBDIR}/slic3r/Utils/ASCIIFolding.cpp ${LIBDIR}/slic3r/Utils/ASCIIFolding.hpp + ${LIBDIR}/slic3r/Utils/Serial.cpp + ${LIBDIR}/slic3r/Utils/Serial.hpp ${LIBDIR}/slic3r/GUI/ConfigWizard.cpp ${LIBDIR}/slic3r/GUI/ConfigWizard.hpp ${LIBDIR}/slic3r/GUI/MsgDialog.cpp @@ -440,7 +442,7 @@ endif() # Add the OpenGL and GLU libraries. if (SLIC3R_GUI) if (MSVC) - target_link_libraries(XS OpenGL32.Lib GlU32.Lib) + target_link_libraries(XS user32.lib Setupapi.lib OpenGL32.Lib GlU32.Lib) elseif (MINGW) target_link_libraries(XS -lopengl32) elseif (APPLE) diff --git a/xs/src/slic3r/GUI/FirmwareDialog.cpp b/xs/src/slic3r/GUI/FirmwareDialog.cpp index b8c3bd49d..8ea9d2d6e 100644 --- a/xs/src/slic3r/GUI/FirmwareDialog.cpp +++ b/xs/src/slic3r/GUI/FirmwareDialog.cpp @@ -23,6 +23,7 @@ #include "libslic3r/Utils.hpp" #include "avrdude/avrdude-slic3r.hpp" #include "GUI.hpp" +#include "../Utils/Serial.hpp" namespace fs = boost::filesystem; @@ -58,6 +59,7 @@ struct FirmwareDialog::priv FirmwareDialog *q; // PIMPL back pointer ("Q-Pointer") wxComboBox *port_picker; + std::vector ports; wxFilePickerCtrl *hex_picker; wxStaticText *txt_status; wxStaticText *txt_progress; @@ -95,13 +97,22 @@ struct FirmwareDialog::priv void FirmwareDialog::priv::find_serial_ports() { - auto ports = GUI::scan_serial_ports(); - - port_picker->Clear(); - for (const auto &port : ports) { port_picker->Append(port); } - - if (ports.size() > 0 && port_picker->GetValue().IsEmpty()) { - port_picker->SetSelection(0); + auto new_ports = Utils::scan_serial_ports_extended(); + if (new_ports != this->ports) { + this->ports = new_ports; + port_picker->Clear(); + for (const auto &port : this->ports) + port_picker->Append(port.friendly_name); + if (ports.size() > 0) { + int idx = port_picker->GetValue().IsEmpty() ? 0 : -1; + for (int i = 0; i < (int)this->ports.size(); ++ i) + if (this->ports[i].is_printer) { + idx = i; + break; + } + if (idx != -1) + port_picker->SetSelection(idx); + } } } @@ -140,9 +151,15 @@ void FirmwareDialog::priv::flashing_status(bool value, AvrDudeComplete complete) void FirmwareDialog::priv::perform_upload() { - auto filename = hex_picker->GetPath(); - auto port = port_picker->GetValue(); - if (filename.IsEmpty() || port.IsEmpty()) { return; } + auto filename = hex_picker->GetPath(); + std::string port = port_picker->GetValue().ToStdString(); + int selection = port_picker->GetSelection(); + if (selection != -1) { + // Verify whether the combo box list selection equals to the combo box edit value. + if (this->ports[selection].friendly_name == port) + port = this->ports[selection].port; + } + if (filename.IsEmpty() || port.empty()) { return; } flashing_status(true); @@ -150,7 +167,7 @@ void FirmwareDialog::priv::perform_upload() "-v", "-p", "atmega2560", "-c", "wiring", - "-P", port.ToStdString(), + "-P", port, "-b", "115200", // XXX: is this ok to hardcode? "-D", "-U", (boost::format("flash:w:%1%:i") % filename.ToStdString()).str() diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp index d3342d22a..d453a2c11 100644 --- a/xs/src/slic3r/GUI/GUI.cpp +++ b/xs/src/slic3r/GUI/GUI.cpp @@ -4,7 +4,6 @@ #include #include -#include #include #include #include @@ -25,7 +24,6 @@ #undef max #endif #include "boost/nowide/convert.hpp" -#pragma comment(lib, "user32.lib") #endif #include @@ -88,83 +86,6 @@ void enable_screensaver() #endif } -std::vector scan_serial_ports() -{ - std::vector out; -#ifdef _WIN32 - // 1) Open the registry key SERIALCOM. - HKEY hKey; - LONG lRes = ::RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"HARDWARE\\DEVICEMAP\\SERIALCOMM", 0, KEY_READ, &hKey); - assert(lRes == ERROR_SUCCESS); - if (lRes == ERROR_SUCCESS) { - // 2) Get number of values of SERIALCOM key. - DWORD cValues; // number of values for key - { - TCHAR achKey[255]; // buffer for subkey name - DWORD cbName; // size of name string - TCHAR achClass[MAX_PATH] = TEXT(""); // buffer for class name - DWORD cchClassName = MAX_PATH; // size of class string - DWORD cSubKeys=0; // number of subkeys - DWORD cbMaxSubKey; // longest subkey size - DWORD cchMaxClass; // longest class string - DWORD cchMaxValue; // longest value name - DWORD cbMaxValueData; // longest value data - DWORD cbSecurityDescriptor; // size of security descriptor - FILETIME ftLastWriteTime; // last write time - // Get the class name and the value count. - lRes = RegQueryInfoKey( - hKey, // key handle - achClass, // buffer for class name - &cchClassName, // size of class string - NULL, // reserved - &cSubKeys, // number of subkeys - &cbMaxSubKey, // longest subkey size - &cchMaxClass, // longest class string - &cValues, // number of values for this key - &cchMaxValue, // longest value name - &cbMaxValueData, // longest value data - &cbSecurityDescriptor, // security descriptor - &ftLastWriteTime); // last write time - assert(lRes == ERROR_SUCCESS); - } - // 3) Read the SERIALCOM values. - { - DWORD dwIndex = 0; - for (int i = 0; i < cValues; ++ i, ++ dwIndex) { - wchar_t valueName[2048]; - DWORD valNameLen = 2048; - DWORD dataType; - wchar_t data[2048]; - DWORD dataSize = 4096; - lRes = ::RegEnumValueW(hKey, dwIndex, valueName, &valNameLen, nullptr, &dataType, (BYTE*)&data, &dataSize); - if (lRes == ERROR_SUCCESS && dataType == REG_SZ && valueName[0] != 0) - out.emplace_back(boost::nowide::narrow(data)); - } - } - ::RegCloseKey(hKey); - } -#else - // UNIX and OS X - std::initializer_list prefixes { "ttyUSB" , "ttyACM", "tty.", "cu.", "rfcomm" }; - for (auto &dir_entry : boost::filesystem::directory_iterator(boost::filesystem::path("/dev"))) { - std::string name = dir_entry.path().filename().string(); - for (const char *prefix : prefixes) { - if (boost::starts_with(name, prefix)) { - out.emplace_back(dir_entry.path().string()); - break; - } - } - } -#endif - - out.erase(std::remove_if(out.begin(), out.end(), - [](const std::string &key){ - return boost::starts_with(key, "Bluetooth") || boost::starts_with(key, "FireFly"); - }), - out.end()); - return out; -} - bool debugged() { #ifdef _WIN32 diff --git a/xs/src/slic3r/GUI/GUI.hpp b/xs/src/slic3r/GUI/GUI.hpp index 56a43b62d..ce3c05616 100644 --- a/xs/src/slic3r/GUI/GUI.hpp +++ b/xs/src/slic3r/GUI/GUI.hpp @@ -68,7 +68,6 @@ inline t_file_wild_card& get_file_wild_card() { void disable_screensaver(); void enable_screensaver(); -std::vector scan_serial_ports(); bool debugged(); void break_to_debugger(); diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp index 38c97f25e..ab5aa2bb0 100644 --- a/xs/src/slic3r/GUI/Tab.cpp +++ b/xs/src/slic3r/GUI/Tab.cpp @@ -6,6 +6,7 @@ #include "slic3r/Utils/Http.hpp" #include "slic3r/Utils/OctoPrint.hpp" +#include "slic3r/Utils/Serial.hpp" #include "BonjourDialog.hpp" #include "WipeTowerDialog.hpp" #include "ButtonsDescription.hpp" @@ -1693,7 +1694,7 @@ void TabPrinter::build() void TabPrinter::update_serial_ports(){ Field *field = get_field("serial_port"); Choice *choice = static_cast(field); - choice->set_values(scan_serial_ports()); + choice->set_values(Utils::scan_serial_ports()); } void TabPrinter::extruders_count_changed(size_t extruders_count){ diff --git a/xs/src/slic3r/Utils/Serial.cpp b/xs/src/slic3r/Utils/Serial.cpp new file mode 100644 index 000000000..57e52318d --- /dev/null +++ b/xs/src/slic3r/Utils/Serial.cpp @@ -0,0 +1,122 @@ +#include "Serial.hpp" + +#include +#include +#include + +#include + +#if _WIN32 +#include +#include +#include +#include +// Undefine min/max macros incompatible with the standard library +// For example, std::numeric_limits::max() +// produces some weird errors +#ifdef min +#undef min +#endif +#ifdef max +#undef max +#endif +#include "boost/nowide/convert.hpp" +#pragma comment(lib, "user32.lib") +#endif + +namespace Slic3r { +namespace Utils { + +static bool looks_like_printer(const std::string &friendly_name) +{ + return friendly_name.find("Original Prusa") != std::string::npos; +} + +std::vector scan_serial_ports_extended() +{ + std::vector output; + +#ifdef _WIN32 + SP_DEVINFO_DATA devInfoData = { 0 }; + devInfoData.cbSize = sizeof(devInfoData); + // Get the tree containing the info for the ports. + HDEVINFO hDeviceInfo = SetupDiGetClassDevs(&GUID_DEVCLASS_PORTS, 0, nullptr, DIGCF_PRESENT); + if (hDeviceInfo != INVALID_HANDLE_VALUE) { + // Iterate over all the devices in the tree. + for (int nDevice = 0; SetupDiEnumDeviceInfo(hDeviceInfo, nDevice, &devInfoData); ++ nDevice) { + SerialPortInfo port_info = {}; + // Get the registry key which stores the ports settings. + HKEY hDeviceKey = SetupDiOpenDevRegKey(hDeviceInfo, &devInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_QUERY_VALUE); + if (hDeviceKey) { + // Read in the name of the port. + wchar_t pszPortName[4096]; + DWORD dwSize = sizeof(pszPortName); + DWORD dwType = 0; + if (RegQueryValueEx(hDeviceKey, L"PortName", NULL, &dwType, (LPBYTE)pszPortName, &dwSize) == ERROR_SUCCESS) + port_info.port = boost::nowide::narrow(pszPortName); + RegCloseKey(hDeviceKey); + if (port_info.port.empty()) + continue; + } + // Find the size required to hold the device info. + DWORD regDataType; + DWORD reqSize = 0; + SetupDiGetDeviceRegistryProperty(hDeviceInfo, &devInfoData, SPDRP_HARDWAREID, nullptr, nullptr, 0, &reqSize); + std::vector hardware_id(reqSize > 1 ? reqSize : 1); + // Now store it in a buffer. + if (! SetupDiGetDeviceRegistryProperty(hDeviceInfo, &devInfoData, SPDRP_HARDWAREID, ®DataType, (BYTE*)hardware_id.data(), reqSize, nullptr)) + continue; + port_info.hardware_id = boost::nowide::narrow(hardware_id.data()); + // Find the size required to hold the friendly name. + reqSize = 0; + SetupDiGetDeviceRegistryProperty(hDeviceInfo, &devInfoData, SPDRP_FRIENDLYNAME, nullptr, nullptr, 0, &reqSize); + std::vector friendly_name; + friendly_name.reserve(reqSize > 1 ? reqSize : 1); + // Now store it in a buffer. + if (! SetupDiGetDeviceRegistryProperty(hDeviceInfo, &devInfoData, SPDRP_FRIENDLYNAME, nullptr, (BYTE*)friendly_name.data(), reqSize, nullptr)) { + port_info.friendly_name = port_info.port; + } else { + port_info.friendly_name = boost::nowide::narrow(friendly_name.data()); + port_info.is_printer = looks_like_printer(port_info.friendly_name); + } + output.emplace_back(std::move(port_info)); + } + } +#else + // UNIX and OS X + std::initializer_list prefixes { "ttyUSB" , "ttyACM", "tty.", "cu.", "rfcomm" }; + for (auto &dir_entry : boost::filesystem::directory_iterator(boost::filesystem::path("/dev"))) { + std::string name = dir_entry.path().filename().string(); + for (const char *prefix : prefixes) { + if (boost::starts_with(name, prefix)) { + SerialPortInfo spi; + spi.port = dir_entry.path().string(); + spi.hardware_id = port; + spi.friendly_name = spi.port; + out.emplace_back(std::move(spi)); + break; + } + } + } +#endif + + output.erase(std::remove_if(output.begin(), output.end(), + [](const SerialPortInfo &info) { + return boost::starts_with(info.port, "Bluetooth") || boost::starts_with(info.port, "FireFly"); + }), + output.end()); + return output; +} + +std::vector scan_serial_ports() +{ + std::vector ports = scan_serial_ports_extended(); + std::vector output; + output.reserve(ports.size()); + for (const SerialPortInfo &spi : ports) + output.emplace_back(std::move(spi.port)); + return output; +} + +} // namespace Utils +} // namespace Slic3r diff --git a/xs/src/slic3r/Utils/Serial.hpp b/xs/src/slic3r/Utils/Serial.hpp new file mode 100644 index 000000000..583661ae5 --- /dev/null +++ b/xs/src/slic3r/Utils/Serial.hpp @@ -0,0 +1,30 @@ +#ifndef slic3r_GUI_Utils_Serial_hpp_ +#define slic3r_GUI_Utils_Serial_hpp_ + +#include +#include + +namespace Slic3r { +namespace Utils { + +struct SerialPortInfo { + std::string port; + std::string hardware_id; + std::string friendly_name; + bool is_printer; +}; + +inline bool operator==(const SerialPortInfo &sp1, const SerialPortInfo &sp2) +{ + return sp1.port == sp2.port && + sp1.hardware_id == sp2.hardware_id && + sp1.is_printer == sp2.is_printer; +} + +extern std::vector scan_serial_ports(); +extern std::vector scan_serial_ports_extended(); + +} // Utils +} // Slic3r + +#endif /* slic3r_GUI_Utils_Serial_hpp_ */ diff --git a/xs/xsp/GUI.xsp b/xs/xsp/GUI.xsp index 2eb24ec7f..f45ddfdfc 100644 --- a/xs/xsp/GUI.xsp +++ b/xs/xsp/GUI.xsp @@ -4,6 +4,7 @@ #include #include "slic3r/GUI/GUI.hpp" #include "slic3r/Utils/ASCIIFolding.hpp" +#include "slic3r/Utils/Serial.hpp" %} @@ -19,7 +20,7 @@ void enable_screensaver() %code{% Slic3r::GUI::enable_screensaver(); %}; std::vector scan_serial_ports() - %code{% RETVAL=Slic3r::GUI::scan_serial_ports(); %}; + %code{% RETVAL=Slic3r::Utils::scan_serial_ports(); %}; bool debugged() %code{% RETVAL=Slic3r::GUI::debugged(); %};