#pragma once #include #include #include #include #include #include #include #include class ControlServer { public: struct Callbacks { std::function getStateJson; std::function selectShader; std::function updateParameter; std::function resetParameters; std::function setBypass; std::function setMixAmount; std::function reloadShader; }; ControlServer(); ~ControlServer(); bool Start(const std::filesystem::path& uiRoot, unsigned short preferredPort, const Callbacks& callbacks, std::string& error); void Stop(); void BroadcastState(); unsigned short GetPort() const { return mPort; } private: struct ClientConnection { SOCKET socket = INVALID_SOCKET; bool websocket = false; }; void ServerLoop(); bool HandleHttpClient(SOCKET clientSocket); bool TryAcceptClient(); bool SendHttpResponse(SOCKET clientSocket, const std::string& status, const std::string& contentType, const std::string& body); bool HandleHttpRequest(SOCKET clientSocket, const std::string& request); bool HandleWebSocketUpgrade(SOCKET clientSocket, const std::string& request); bool SendWebSocketText(SOCKET clientSocket, const std::string& payload); void BroadcastStateLocked(); std::string LoadUiAsset(const std::string& relativePath, std::string& contentType) const; std::string BuildJsonResponse(bool success, const std::string& error = std::string()) const; static std::string Base64Encode(const unsigned char* data, DWORD dataLength); static std::string ComputeWebSocketAcceptKey(const std::string& clientKey); static std::string GetHeaderValue(const std::string& request, const std::string& headerName); static std::string GetRequestPath(const std::string& request); static std::string GetRequestMethod(const std::string& request); static std::string GetRequestBody(const std::string& request); private: std::filesystem::path mUiRoot; Callbacks mCallbacks; SOCKET mListenSocket; unsigned short mPort; std::thread mThread; std::atomic mRunning; mutable std::mutex mMutex; std::vector mClients; };