#pragma once #include #include #include #include #include // IN-01 (iter-3): MdnsImpl is the per-instance helper state (service blocks, // hostname strings, sockaddr) held by MdnsAdvertiser. Declared opaque in the // header so the C-ABI mdns types do not leak through to callers — the full // definition lives in mdns_advertise.cpp. Zero-overhead forward-decl + unique_ptr // replaces the former file-scope `g_impl` global that silently prevented // multiple MdnsAdvertiser instances from coexisting. struct MdnsImpl; class MdnsAdvertiser { public: MdnsAdvertiser(); ~MdnsAdvertiser(); // Advertises both `_oscjson._tcp` (at httpTcpPort) and `_osc._udp` (at oscUdpPort) // on 127.0.0.1. serviceName is the mDNS instance name ("Beyond Backglow"). bool Start(uint16_t oscUdpPort, uint16_t httpTcpPort, const char* serviceName); void Stop(); private: void ListenLoop(); std::thread m_thread; std::atomic m_stop{false}; int m_socket{-1}; // mdns socket fd (intptr on Windows) // IN-01 (iter-3) fix: own the MdnsImpl state per-instance instead of via a // file-scope `g_impl` global. Multiple MdnsAdvertiser instances (test // harness, multi-instance daemon mode) can now coexist. The double-start // guard in Start() becomes an instance-local check (m_impl != nullptr). std::unique_ptr m_impl; // IN-03 (re-review): removed unused m_instanceName / m_oscUdpPort / // m_httpTcpPort. All three were assigned in Start() but never read; the // live service state lives in the per-instance MdnsImpl struct in the .cpp // and is fully sufficient for the announce + goodbye paths. If a future // re-announce or reconnect path ever needs to reference these, reintroduce // them at that time rather than carrying dead state now. };