diff --git a/src/Pudding.cpp b/src/Pudding.cpp index 5c88fd6..e1feb35 100644 --- a/src/Pudding.cpp +++ b/src/Pudding.cpp @@ -39,6 +39,21 @@ void set_heap_offset(int offset) /* Initialize a Pudding instance */ Pudding::Pudding() { +#ifndef __EMSCRIPTEN__ + /* Initialize cURL and store result. Initialize the multi request handler. */ + curl_init_result = curl_global_init(CURL_GLOBAL_DEFAULT); + if (curl_init_result != CURLE_OK) + { + std::ostringstream message; + message << "cURL failed to initialize and will not be available " << curl_easy_strerror(curl_init_result); + sb::Log::log(message); + } + else + { + curl_multi_handle.reset(curl_multi_init()); + } +#endif + /* subscribe to command events */ get_delegate().subscribe(&Pudding::respond, this); get_delegate().subscribe(&Pudding::respond, this, SDL_MOUSEMOTION); @@ -589,7 +604,7 @@ void Pudding::incorporate_nutritionix_api(const std::vector& json_ sb::Log::log(json_formatted.str(), sb::Log::DEBUG); /* test that should determine if a Nutritionix response is not empty */ - if (!(json.contains("message") && json["message"] == NUTRITIONIX_NOT_FOUND)) + if (json.contains("foods")) { nlohmann::json food = json["foods"][0]; std::ostringstream message; @@ -820,22 +835,29 @@ void Pudding::web_get_bytes(std::string url, const web_callback& callback, const #else - CURL *curl; - CURLcode result; - result = curl_global_init(CURL_GLOBAL_DEFAULT); - if (result != CURLE_OK) + if (curl_init_result != CURLE_OK) { - std::cout << "cURL initialization failed " << curl_easy_strerror(result) << std::endl; + std::ostringstream message; + message << "cURL unavailable " << curl_easy_strerror(curl_init_result); + sb::Log::log(message); } else { - curl = curl_easy_init(); - if (curl) + CURL* curl_easy_handle = curl_easy_init(); + + if (curl_easy_handle) { - curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_response); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, request); - curl_easy_setopt(curl, CURLOPT_USERAGENT, configuration()["api"]["user-agent"].get().c_str()); + curl_easy_setopt(curl_easy_handle, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl_easy_handle, CURLOPT_WRITEFUNCTION, curl_write_response); + + /* WRITEDATA is set to the request object, so the storage in the request object can receive the transfer data */ + curl_easy_setopt(curl_easy_handle, CURLOPT_WRITEDATA, request); + + /* PRIVATE is also set to the request object, so the request can call the response function when the multi handle has + * determined that all the transfer packets are complete */ + curl_easy_setopt(curl_easy_handle, CURLOPT_PRIVATE, request); + + curl_easy_setopt(curl_easy_handle, CURLOPT_USERAGENT, configuration()["api"]["user-agent"].get().c_str()); /* Pass submitted headers to cURL */ struct curl_slist* list = nullptr; @@ -849,26 +871,18 @@ void Pudding::web_get_bytes(std::string url, const web_callback& callback, const list = curl_slist_append(list, pair.c_str()); } } - curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list); + curl_easy_setopt(curl_easy_handle, CURLOPT_HTTPHEADER, list); - result = curl_easy_perform(curl); - curl_slist_free_all(list); - - if (result != CURLE_OK) - { - std::cout << "cURL request failed " << curl_easy_strerror(result) << std::endl; - } + /* Add the easy handle to the multi handle, so it can be processed asynchronously in the update loop */ + curl_multi_add_handle(curl_multi_handle.get(), curl_easy_handle); } else { - std::cout << "cURL initialization failed" << std::endl; + std::ostringstream message; + message << "cURL request initialization failed for " << url; + sb::Log::log(message); } - curl_easy_cleanup(curl); } - curl_global_cleanup(); - - /* Call the user supplied callback */ - request->respond(); #endif @@ -1334,6 +1348,28 @@ void Pudding::update() previous_barcode = current_barcode; } +#ifndef __EMSCRIPTEN__ + /* Calling multi perform will perform transfers on handles cURL has determined are ready for transfer. Any handles that have transferred + * all their necessary data will cause a CURLMSG_DONE message to be in the message queue. If a handle is done, call the request callback + * that has been attached to it, remove the handle, and clean it up. */ + int running_handles; + curl_multi_perform(curl_multi_handle.get(), &running_handles); + CURLMsg* message_queue; + do + { + int message_count; + message_queue = curl_multi_info_read(curl_multi_handle.get(), &message_count); + if (message_queue && (message_queue->msg == CURLMSG_DONE)) + { + Request* request; + curl_easy_getinfo(message_queue->easy_handle, CURLINFO_PRIVATE, &request); + request->respond(); + curl_multi_remove_handle(curl_multi_handle.get(), message_queue->easy_handle); + curl_easy_cleanup(message_queue->easy_handle); + } + } while(message_queue); +#endif + /* Delete and erase finished requests from the vector using iterators to erase while reading the vector */ for (auto iter = requests.begin(); iter != requests.end();) { @@ -1378,6 +1414,13 @@ void Pudding::update() } } +Pudding::~Pudding() +{ +#ifndef __EMSCRIPTEN__ + curl_global_cleanup(); +#endif +} + Request::Request(const web_callback& callback, const std::string& url) : callback(callback), request_url(url) {} void Request::store(const std::uint8_t* buffer, const std::size_t& size) diff --git a/src/Pudding.hpp b/src/Pudding.hpp index 6afd30c..d795783 100644 --- a/src/Pudding.hpp +++ b/src/Pudding.hpp @@ -13,6 +13,18 @@ /* Needed for functions in glm/gtx/ */ #define GLM_ENABLE_EXPERIMENTAL +/* Standard library includes */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + /* cURL and cv::VideoCapture are not available for Emscripten, so use alternatives for Emscripten builds */ #if defined(__EMSCRIPTEN__) #include @@ -24,16 +36,6 @@ using namespace emscripten; #include "opencv2/highgui.hpp" #endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include "SDL.h" #include "SDL_image.h" #include "sdl2-gfx/SDL2_gfxPrimitives.h" @@ -429,6 +431,9 @@ private: int effect_id = EFFECT_NONE, pudding_triangle_vertex_count = 0, pudding_fan_vertex_count = 0; #ifndef __EMSCRIPTEN__ cv::VideoCapture capture; + std::unique_ptr curl_multi_handle = std::unique_ptr( + nullptr, curl_multi_cleanup); + CURLcode curl_init_result; #endif cv::Mat camera_frame, contrasted_frame, sharpened_frame, zbar_frame, blurred, low_contrast_mask; zbar::ImageScanner image_scanner; @@ -591,6 +596,7 @@ public: Item& current_item(); void update(); virtual std::string class_name() const { return "Pudding"; } + ~Pudding(); };