#include <string>
#include <sstream>
+#include <boost/algorithm/string.hpp>
#include <curl/curl.h>
#include <libcxml/cxml.h>
#include "update.h"
#include "version.h"
+#include "ui_signaller.h"
#define BUFFER_SIZE 1024
using std::min;
using std::string;
using std::stringstream;
+using boost::lexical_cast;
+
+UpdateChecker* UpdateChecker::_instance = 0;
+
+static size_t
+write_callback_wrapper (void* data, size_t size, size_t nmemb, void* user)
+{
+ return reinterpret_cast<UpdateChecker*>(user)->write_callback (data, size, nmemb);
+}
UpdateChecker::UpdateChecker ()
: _buffer (new char[BUFFER_SIZE])
, _offset (0)
+ , _curl (0)
+ , _state (NOT_RUN)
+ , _startup (true)
{
+ curl_global_init (CURL_GLOBAL_ALL);
+ _curl = curl_easy_init ();
+
+ curl_easy_setopt (_curl, CURLOPT_URL, "http://dcpomatic.com/update");
+ curl_easy_setopt (_curl, CURLOPT_WRITEFUNCTION, write_callback_wrapper);
+ curl_easy_setopt (_curl, CURLOPT_WRITEDATA, this);
+ curl_easy_setopt (_curl, CURLOPT_TIMEOUT, 20);
+ string const agent = "dcpomatic/" + string (dcpomatic_version);
+ curl_easy_setopt (_curl, CURLOPT_USERAGENT, agent.c_str ());
}
UpdateChecker::~UpdateChecker ()
{
+ curl_easy_cleanup (_curl);
+ curl_global_cleanup ();
delete[] _buffer;
}
-static size_t
-write_callback_wrapper (void* data, size_t size, size_t nmemb, void* user)
+void
+UpdateChecker::run (bool startup)
+try
{
- return reinterpret_cast<UpdateChecker*>(user)->write_callback (data, size, nmemb);
-}
+ boost::mutex::scoped_lock lm (_single_thread_mutex);
-UpdateChecker::Result
-UpdateChecker::run ()
-{
- curl_global_init (CURL_GLOBAL_ALL);
- CURL* curl = curl_easy_init ();
- if (!curl) {
- return MAYBE;
+ {
+ boost::mutex::scoped_lock lm (_data_mutex);
+ _startup = startup;
}
-
- curl_easy_setopt (curl, CURLOPT_URL, "http://dcpomatic.com/update.php");
- curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, write_callback_wrapper);
- curl_easy_setopt (curl, CURLOPT_WRITEDATA, this);
- string const agent = "dcpomatic/" + string (dcpomatic_version);
- curl_easy_setopt (curl, CURLOPT_USERAGENT, agent.c_str ());
- int r = curl_easy_perform (curl);
+
+ _offset = 0;
+
+ int r = curl_easy_perform (_curl);
if (r != CURLE_OK) {
- return MAYBE;
+ set_state (FAILED);
+ return;
}
-
- _buffer[BUFFER_SIZE-1] = '\0';
+
+ _buffer[_offset] = '\0';
stringstream s;
s << _buffer;
cxml::Document doc ("Update");
doc.read_stream (s);
- cout << doc.string_child ("Stable") << "\n";
- return YES;
+ {
+ boost::mutex::scoped_lock lm (_data_mutex);
+ _stable = doc.string_child ("Stable");
+ }
+
+ string current = string (dcpomatic_version);
+ bool current_pre = false;
+ if (boost::algorithm::ends_with (current, "pre")) {
+ current = current.substr (0, current.length() - 3);
+ current_pre = true;
+ }
+
+ float current_float = lexical_cast<float> (current);
+ if (current_pre) {
+ current_float -= 0.005;
+ }
+
+ if (current_float < lexical_cast<float> (_stable)) {
+ set_state (YES);
+ } else {
+ set_state (NO);
+ }
+} catch (...) {
+ set_state (FAILED);
}
size_t
UpdateChecker::write_callback (void* data, size_t size, size_t nmemb)
{
- size_t const t = min (size * nmemb, size_t (BUFFER_SIZE - _offset));
+ size_t const t = min (size * nmemb, size_t (BUFFER_SIZE - _offset - 1));
memcpy (_buffer + _offset, data, t);
_offset += t;
return t;
}
+
+void
+UpdateChecker::set_state (State s)
+{
+ {
+ boost::mutex::scoped_lock lm (_data_mutex);
+ _state = s;
+ }
+
+ ui_signaller->emit (boost::bind (boost::ref (StateChanged)));
+}
+
+UpdateChecker *
+UpdateChecker::instance ()
+{
+ if (!_instance) {
+ _instance = new UpdateChecker ();
+ }
+
+ return _instance;
+}
+
+
*/
+#include <boost/signals2.hpp>
+#include <boost/thread/mutex.hpp>
+#include <curl/curl.h>
+
class UpdateChecker
{
public:
UpdateChecker ();
~UpdateChecker ();
- enum Result {
+ void run (bool);
+
+ enum State {
YES,
- MAYBE,
- NO
+ FAILED,
+ NO,
+ NOT_RUN
};
- Result run ();
+ State state () {
+ boost::mutex::scoped_lock lm (_data_mutex);
+ return _state;
+ }
+
+ std::string stable () {
+ boost::mutex::scoped_lock lm (_data_mutex);
+ return _stable;
+ }
+
+ /** @return true if this check was run at startup, otherwise false */
+ bool startup () const {
+ boost::mutex::scoped_lock lm (_data_mutex);
+ return _startup;
+ }
size_t write_callback (void *, size_t, size_t);
+ boost::signals2::signal<void (void)> StateChanged;
+
+ static UpdateChecker* instance ();
+
private:
+ static UpdateChecker* _instance;
+
+ void set_state (State);
+
char* _buffer;
int _offset;
+ CURL* _curl;
+
+ /** mutex to protect _state, _stable and _startup */
+ mutable boost::mutex _data_mutex;
+ State _state;
+ std::string _stable;
+ /** true if this check was run at startup, otherwise false */
+ bool _startup;
+
+ /** mutex to ensure that only one query runs at once */
+ boost::mutex _single_thread_mutex;
};
void tools_check_for_updates ()
{
- UpdateChecker c;
- c.run ();
+ UpdateChecker::instance()->run (false);
}
void help_about ()
film->set_name (boost::filesystem::path (film_to_create).filename().generic_string ());
}
- Frame* f = new Frame (_("DCP-o-matic"));
- SetTopWindow (f);
- f->Maximize ();
- f->Show ();
+ _frame = new Frame (_("DCP-o-matic"));
+ SetTopWindow (_frame);
+ _frame->Maximize ();
+ _frame->Show ();
+
+ UpdateChecker::instance()->StateChanged.connect (boost::bind (&App::update_checker_state_changed, this));
ui_signaller = new wxUISignaller (this);
Bind (wxEVT_IDLE, boost::bind (&App::idle, this));
}
}
+ void update_checker_state_changed ()
+ {
+ switch (UpdateChecker::instance()->state ()) {
+ case UpdateChecker::YES:
+ error_dialog (
+ _frame,
+ wxString::Format (
+ _("A new version %s of DCP-o-matic is available from http://dcpomatic.com/download"),
+ std_to_wx (UpdateChecker::instance()->stable()).wx_str ()
+ )
+ );
+ break;
+ case UpdateChecker::NO:
+ if (!UpdateChecker::instance()->startup ()) {
+ error_dialog (_frame, _("There are no new versions of DCP-o-matic available."));
+ }
+ break;
+ case UpdateChecker::FAILED:
+ if (!UpdateChecker::instance()->startup ()) {
+ error_dialog (_frame, _("The DCP-o-matic download server could not be contacted."));
+ }
+ default:
+ break;
+ }
+ }
+
+ wxFrame* _frame;
shared_ptr<wxTimer> _timer;
};
--- /dev/null
+/*
+ Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+class UpdateDialog
+{
+public:
+ UpdateDialog (wxWindow *);
+
+private:
+ wxGauge* _gauge;
+ wxStaticText* _message;
+ boost::thread* _thread;
+ boost::optional<UpdateChecker::Result> _result;
+ std::string _stable;
+};