commit d077e7643e4710b27225b807305170f255f5614d
parent edb1a9c131391f9a5ca08eb2e713f2f2a42b261f
Author: cfillion <cfillion@users.noreply.github.com>
Date: Mon, 23 Nov 2015 21:14:25 -0500
implement background file downloading
Diffstat:
4 files changed, 244 insertions(+), 2 deletions(-)
diff --git a/Tupfile b/Tupfile
@@ -9,12 +9,17 @@ CXXFLAGS += -DWDL_NO_DEFINE_MINMAX
LDFLAGS := -dynamiclib -framework Cocoa -framework Carbon
WDL := vendor/WDL/WDL
-SWELL := $(WDL)/swell
+SWELL := $(WDL)/swell
WDLSOURCE := $(SWELL)/swell.cpp $(SWELL)/swell-ini.cpp
WDLSOURCE += $(SWELL)/swell-gdi.mm $(SWELL)/swell-kb.mm $(SWELL)/swell-menu.mm
WDLSOURCE += $(SWELL)/swell-misc.mm $(SWELL)/swell-dlg.mm $(SWELL)/swell-wnd.mm
+JNETLIB := $(WDL)/jnetlib
+WDLSOURCE += $(JNETLIB)/util.cpp $(JNETLIB)/httpget.cpp
+WDLSOURCE += $(JNETLIB)/connection.cpp $(JNETLIB)/asyncdns.cpp
+
+
!build = |> $(CXX) $(CXXFLAGS) -c %f -o %o |>
!link = |> $(CXX) $(CXXFLAGS) %f $(LDFLAGS) -o %o |>
diff --git a/src/download.cpp b/src/download.cpp
@@ -0,0 +1,171 @@
+#include "download.hpp"
+
+#include <cstdlib>
+
+#include <WDL/jnetlib/httpget.h>
+#include <WDL/jnetlib/jnetlib.h>
+
+#include <reaper_plugin_functions.h>
+
+std::vector<Download *> Download::s_active;
+
+static const int DOWNLOAD_TIMEOUT = 60 * 10;
+
+Download::Download(const char *url)
+ : m_threadHandle(0), m_stop(false), m_status(0)
+{
+ m_url = url;
+}
+
+Download::~Download()
+{
+ stop();
+}
+
+void Download::addCallback(const DownloadCallback &callback)
+{
+ m_callback.push_back(callback);
+}
+
+void Download::timerTick() // static
+{
+ std::vector<Download *> &activeDownloads = Download::s_active;
+ const int size = activeDownloads.size();
+
+ for(int i = 0; i < size; i++) {
+ Download *download = activeDownloads[i];
+
+ if(!download->isFinished())
+ continue;
+
+ // this need to be done before execCallback in case one of the callback
+ // deletes the download object
+ activeDownloads.erase(activeDownloads.begin() + i);
+
+ download->execCallbacks();
+ }
+
+ if(Download::s_active.empty())
+ plugin_register("-timer", (void*)timerTick);
+}
+
+
+Download::StartCode Download::start()
+{
+ if(m_threadHandle)
+ return AlreadyRunning;
+
+ s_active.push_back(this);
+ plugin_register("timer", (void*)timerTick);
+
+ m_threadHandle = CreateThread(NULL, 0, worker, (void *)this, 0, 0);
+ return Started;
+}
+
+void Download::stop()
+{
+ WDL_MutexLock lock(&m_mutex);
+
+ if(!m_threadHandle)
+ return;
+
+ m_stop = true;
+
+ if(!isFinished())
+ s_active.erase(std::remove(s_active.begin(), s_active.end(), this));
+
+ WaitForSingleObject(m_threadHandle, INFINITE);
+ CloseHandle(m_threadHandle);
+ m_threadHandle = 0;
+
+ m_stop = false;
+};
+
+DWORD WINAPI Download::worker(void *ptr) // static
+{
+ JNL::open_socketlib();
+
+ Download *download = static_cast<Download *>(ptr);
+
+ JNL_HTTPGet agent;
+ agent.addheader("Accept: */*");
+ agent.addheader("User-Agent: ReaPack (REAPER)");
+ agent.connect(download->url());
+
+ const time_t startTime = time(NULL);
+
+ while(agent.run() == 0) {
+ if(download->isCancelled()) {
+ download->finish(-2, "");
+ JNL::close_socketlib();
+ return 1;
+ }
+ else if(time(NULL) - startTime >= DOWNLOAD_TIMEOUT) {
+ download->finish(-3, "");
+ JNL::close_socketlib();
+ return 1;
+ }
+
+ Sleep(50);
+ }
+
+ const int status = agent.getreplycode();
+ const int size = agent.bytes_available();
+
+ char *buffer = new char[size];
+ agent.get_bytes(buffer, size);
+
+ download->finish(status, buffer);
+
+ delete[] buffer;
+
+ JNL::close_socketlib();
+
+ return 0;
+}
+
+void Download::finish(const int status, const std::string &contents)
+{
+ WDL_MutexLock lock(&m_mutex);
+
+ // called from the worker thread
+ m_finished = true;
+ m_status = status;
+ m_contents = contents;
+}
+
+void Download::execCallbacks()
+{
+ // always called from the main thread
+
+ for(DownloadCallback callback : m_callback)
+ callback(status(), contents());
+}
+
+int Download::status()
+{
+ WDL_MutexLock lock(&m_mutex);
+
+ return m_status;
+}
+
+const std::string &Download::contents()
+{
+ WDL_MutexLock lock(&m_mutex);
+
+ return m_contents;
+}
+
+bool Download::isFinished()
+{
+ WDL_MutexLock lock(&m_mutex);
+
+ return m_finished;
+}
+
+bool Download::isCancelled()
+{
+ WDL_MutexLock lock(&m_mutex);
+
+ return m_stop;
+}
diff --git a/src/download.hpp b/src/download.hpp
@@ -0,0 +1,58 @@
+#ifndef REAPACK_DOWNLOAD_HPP
+#define REAPACK_DOWNLOAD_HPP
+
+#include <functional>
+#include <string>
+#include <vector>
+
+#include <WDL/mutex.h>
+
+#include <reaper_plugin.h>
+
+typedef std::function<void(int, const std::string &)> DownloadCallback;
+
+class Download {
+public:
+ enum StartCode {
+ Started,
+ AlreadyRunning,
+ FileExists,
+ WriteError,
+ };
+
+ Download(const char *url);
+ ~Download();
+
+ const char *url() const { return m_url; }
+
+ bool isFinished();
+ bool isCancelled();
+ int status();
+ const std::string &contents();
+
+ void addCallback(const DownloadCallback &);
+ StartCode start();
+ void stop();
+
+private:
+ static std::vector<Download *> s_active;
+
+ static void timerTick();
+ static DWORD WINAPI worker(void *ptr);
+
+ void finish(const int status, const std::string &contents);
+ void execCallbacks();
+
+ HANDLE m_threadHandle;
+ bool m_stop;
+ bool m_finished;
+ int m_status;
+ std::string m_contents;
+
+ const char *m_url;
+ std::vector<DownloadCallback> m_callback;
+
+ WDL_Mutex m_mutex;
+};
+
+#endif
diff --git a/src/main.cpp b/src/main.cpp
@@ -1,7 +1,8 @@
#include "reapack.hpp"
+#include "download.hpp"
#define REAPERAPI_IMPLEMENT
-#include "reaper_plugin_functions.h"
+#include <reaper_plugin_functions.h>
static ReaPack reapack;
@@ -19,6 +20,13 @@ extern "C" REAPER_PLUGIN_DLL_EXPORT int REAPER_PLUGIN_ENTRYPOINT(
if(REAPERAPI_LoadAPI(rec->GetFunc) > 0)
return 0;
+ Download *down = new Download("http://cfillion.tk/");
+ down->addCallback([=](const int status, const std::string &contents) {
+ ShowMessageBox(contents.c_str(), std::to_string(status).c_str(), 0);
+ delete down;
+ });
+ down->start();
+
reapack.init(instance, rec);
reapack.setupAction("REAPACKMGR", "ReaPack: Package Manager",