diff --git a/CMakeLists.txt b/CMakeLists.txt index 932e951..2e9d6f8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,10 +65,11 @@ if(WIN32) # Use static runtime to avoid VC++ Redistributable dependency set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") else() - # MinGW/GCC flags - set(CMAKE_C_STANDARD_LIBRARIES "-lsetupapi -static-libgcc -static-libstdc++ -lwsock32 -lws2_32 ${CMAKE_CXX_STANDARD_LIBRARIES}") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-Bstatic,--whole-archive -lwinpthread -Wl,--no-whole-archive") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") + # MinGW/GCC flags - statically link runtime to avoid DLL dependencies + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libgcc -static-libstdc++ -Wl,-Bstatic,--whole-archive -lwinpthread -Wl,--no-whole-archive") + set(CMAKE_C_STANDARD_LIBRARIES "-lsetupapi -lwsock32 -lws2_32 ${CMAKE_C_STANDARD_LIBRARIES}") + set(CMAKE_CXX_STANDARD_LIBRARIES "-lsetupapi -lwsock32 -lws2_32 ${CMAKE_CXX_STANDARD_LIBRARIES}") + add_compile_options(-Wall) endif() else() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") diff --git a/cli/main.cpp b/cli/main.cpp index 397f234..0011ec6 100644 --- a/cli/main.cpp +++ b/cli/main.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -1101,6 +1102,12 @@ bool checkDeviceConnected(DiscoveredDevice& selected, const Options& opts) int main(int argc, char* argv[]) { +#if defined(_WIN32) && defined(__GNUC__) + // Disable stdout/stderr buffering for MinGW builds + setvbuf(stdout, nullptr, _IONBF, 0); + setvbuf(stderr, nullptr, _IONBF, 0); +#endif + Options opts; cli::ArgumentParser parser(argv[0]); configureParser(parser, opts); diff --git a/lib/devices/steelseries_arctis_nova_7.hpp b/lib/devices/steelseries_arctis_nova_7.hpp index 713135c..88bdfc8 100644 --- a/lib/devices/steelseries_arctis_nova_7.hpp +++ b/lib/devices/steelseries_arctis_nova_7.hpp @@ -29,15 +29,16 @@ namespace headsetcontrol { */ class SteelSeriesArctisNova7 : public protocols::SteelSeriesNovaDevice { public: - static constexpr std::array SUPPORTED_PRODUCT_IDS { - 0x2202, // Arctis Nova 7 - 0x227e, // Arctis Nova 7 Wireless - 0x2206, // Arctis Nova 7x - 0x2258, // Arctis Nova 7x v2 - 0x229e, // Arctis Nova 7x v2 - 0x223a, // Arctis Nova 7 Diablo IV (before Jan Update) - 0x22a9, // Arctis Nova 7 Diablo IV (after Jan Update) - 0x227a // Arctis Nova 7 WoW Edition + static constexpr std::array SUPPORTED_PRODUCT_IDS { + 0x2202, // Arctis Nova 7 (discrete battery: 0-4) + 0x22A1, // Arctis Nova 7 (percentage battery: 0-100, Jan. 2026 update) + 0x227e, // Arctis Nova 7 Wireless Gen 2 (percentage battery: 0-100) + 0x2206, // Arctis Nova 7x (discrete battery: 0-4) + 0x2258, // Arctis Nova 7x v2 (percentage battery: 0-100) + 0x229e, // Arctis Nova 7x v2 (percentage battery: 0-100) + 0x223a, // Arctis Nova 7 Diablo IV (discrete battery: 0-4, before Jan 2026 update) + 0x22a9, // Arctis Nova 7 Diablo IV (percentage battery: 0-100, after Jan 2026 update) + 0x227a // Arctis Nova 7 WoW Edition (discrete battery: 0-4) }; static constexpr int EQUALIZER_BANDS = 10; @@ -122,12 +123,44 @@ class SteelSeriesArctisNova7 : public protocols::SteelSeriesNovaDevice 4 → Gen 2 protocol (wouldn't be valid in discrete mode) + // + // TODO: Known edge case - Gen 2 at 1-4% battery while actively charging (status=0x01) + // will be misdetected as original protocol and show inflated percentage + // (1%→25%, 2%→50%, 3%→75%, 4%→100%). This is extremely rare because: + // - Requires plugging in exactly at 1-4% battery + // - At low battery, devices typically show status=0x03 (on battery) + // - Self-corrects once battery charges past 4% + // - Only lasts a few seconds/minutes + // Proper fix would require passing product_id to getBattery() method. + bool is_gen2_protocol = (data[3] == 0x02 || data[3] == 0x03) || (data[2] > 4); + enum battery_status status = BATTERY_AVAILABLE; - if (data[3] == 0x01) { + if (data[3] == 0x01 || data[3] == 0x02) { status = BATTERY_CHARGING; } - int level = map(data[2], 0, 4, 0, 100); + int level; + if (is_gen2_protocol) { + // Gen 2: Direct percentage reporting (0-100) in data[2] + level = data[2]; + } else { + // Original models: Discrete levels (0-4) that need mapping + level = map(data[2], 0, 4, 0, 100); + } + if (level > 100) level = 100;