diff options
author | Thiago Marcos P. Santos <thiago@mapbox.com> | 2015-09-01 14:05:59 +0300 |
---|---|---|
committer | Thiago Marcos P. Santos <thiago@mapbox.com> | 2016-04-20 20:55:51 +0300 |
commit | e93d51c922a0d55b6f40b07185452dc54151736d (patch) | |
tree | 0042be118f388dfdbe590b06cf2c4efe4ef1b1cf /platform | |
parent | 2af3863b5716b290c852e2dae93ca023cb6bf0f4 (diff) | |
download | qtlocation-mapboxgl-e93d51c922a0d55b6f40b07185452dc54151736d.tar.gz |
[Qt] Implement the AsyncTask, Timer and RunLoop using Qt
Now Mapbox GL will handle events using Qt as backend
instead of libuv.
Diffstat (limited to 'platform')
-rw-r--r-- | platform/qt/src/async_task.cpp | 39 | ||||
-rw-r--r-- | platform/qt/src/async_task_impl.hpp | 38 | ||||
-rw-r--r-- | platform/qt/src/run_loop.cpp | 144 | ||||
-rw-r--r-- | platform/qt/src/run_loop_impl.hpp | 39 | ||||
-rw-r--r-- | platform/qt/src/timer.cpp | 57 | ||||
-rw-r--r-- | platform/qt/src/timer_impl.hpp | 31 |
6 files changed, 348 insertions, 0 deletions
diff --git a/platform/qt/src/async_task.cpp b/platform/qt/src/async_task.cpp new file mode 100644 index 0000000000..cfad70d8dd --- /dev/null +++ b/platform/qt/src/async_task.cpp @@ -0,0 +1,39 @@ +#include <mbgl/util/async_task.hpp> + +#include "async_task_impl.hpp" + +#include <mbgl/util/run_loop.hpp> + +namespace mbgl { +namespace util { + +AsyncTask::Impl::Impl(std::function<void()>&& fn) + : runLoop(RunLoop::Get()), + task(std::move(fn)) { + connect(this, SIGNAL(send(void)), this, SLOT(runTask(void)), Qt::QueuedConnection); +} + +void AsyncTask::Impl::maySend() { + if (!queued.test_and_set()) { + emit send(); + } +} + +void AsyncTask::Impl::runTask() { + queued.clear(); + task(); +} + +AsyncTask::AsyncTask(std::function<void()>&& fn) + : impl(std::make_unique<Impl>(std::move(fn))) { +} + +AsyncTask::~AsyncTask() { +} + +void AsyncTask::send() { + impl->maySend(); +} + +} +} diff --git a/platform/qt/src/async_task_impl.hpp b/platform/qt/src/async_task_impl.hpp new file mode 100644 index 0000000000..171990b15c --- /dev/null +++ b/platform/qt/src/async_task_impl.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include <mbgl/util/async_task.hpp> + +#include <QObject> + +#include <functional> +#include <atomic> + +namespace mbgl { +namespace util { + +class RunLoop; + +class AsyncTask::Impl : public QObject { + Q_OBJECT + +public: + Impl(std::function<void()>&& fn); + + void maySend(); + +public slots: + void runTask(); + +signals: + void send(); + +private: + RunLoop* runLoop; + + std::function<void()> task; + std::atomic_flag queued = ATOMIC_FLAG_INIT; +}; + + +} +} diff --git a/platform/qt/src/run_loop.cpp b/platform/qt/src/run_loop.cpp new file mode 100644 index 0000000000..d33eb9cda6 --- /dev/null +++ b/platform/qt/src/run_loop.cpp @@ -0,0 +1,144 @@ +#include "run_loop_impl.hpp" + +#include <mbgl/util/thread_local.hpp> + +#include <QCoreApplication> + +#include <functional> +#include <utility> + +#include <cassert> + +namespace { + +using namespace mbgl::util; +static ThreadLocal<RunLoop>& current = *new ThreadLocal<RunLoop>; + +} + +namespace mbgl { +namespace util { + +void RunLoop::Impl::onReadEvent(int fd) { + readPoll[fd].second(fd, Event::Read); +} + +void RunLoop::Impl::onWriteEvent(int fd) { + writePoll[fd].second(fd, Event::Write); +} + +RunLoop* RunLoop::Get() { + return current.get(); +} + +RunLoop::RunLoop(Type type) : impl(std::make_unique<Impl>()) { + // XXX: We should probably throw an runtime exception + // here instead of creating a QCoreApplication which is + // way too intrusive. This is a hack mostly for the unit + // tests to work, as you always need a QCoreApplication + // prior to run a Qt app. + if (!QCoreApplication::instance()) { + static const char* argv[] = { "mbgl" }; + static int argc = 1; + + // We need to keep this around because it would otherwise crash + // on Qt4 due to a bug on QNetworkConfigurationManager when recreating + // a QCoreApplication: https://bugreports.qt.io/browse/QTBUG-36897 + static auto* app = new QCoreApplication(argc, const_cast<char**>(argv)); + Q_UNUSED(app); + } + + switch (type) { + case Type::New: + impl->loop = std::make_unique<QEventLoop>(); + break; + case Type::Default: + // Use QCoreApplication::instance(). + break; + } + + impl->type = type; + + current.set(this); + impl->async = std::make_unique<AsyncTask>(std::bind(&RunLoop::process, this)); +} + +RunLoop::~RunLoop() { + MBGL_VERIFY_THREAD(tid); + + current.set(nullptr); +} + +LOOP_HANDLE RunLoop::getLoopHandle() { + throw std::runtime_error("Should not be used in Qt."); + + return nullptr; +} + +void RunLoop::push(std::shared_ptr<WorkTask> task) { + withMutex([&] { queue.push(task); }); + impl->async->send(); +} + +void RunLoop::run() { + MBGL_VERIFY_THREAD(tid); + + if (impl->type == Type::Default) { + QCoreApplication::instance()->exec(); + } else { + impl->loop->exec(); + } +} + +void RunLoop::stop() { + invoke([&] { + if (impl->type == Type::Default) { + QCoreApplication::instance()->exit(); + } else { + impl->loop->exit(); + } + }); +} + +void RunLoop::runOnce() { + MBGL_VERIFY_THREAD(tid); + + if (impl->type == Type::Default) { + QCoreApplication::instance()->processEvents(); + } else { + impl->loop->processEvents(); + } +} + +void RunLoop::addWatch(int fd, Event event, std::function<void(int, Event)>&& cb) { + MBGL_VERIFY_THREAD(tid); + + if (event == Event::Read || event == Event::ReadWrite) { + auto notifier = std::make_unique<QSocketNotifier>(fd, QSocketNotifier::Read); + QObject::connect(notifier.get(), SIGNAL(activated(int)), impl.get(), SLOT(onReadEvent(int))); + impl->readPoll[fd] = WatchPair(std::move(notifier), std::move(cb)); + } + + if (event == Event::Write || event == Event::ReadWrite) { + auto notifier = std::make_unique<QSocketNotifier>(fd, QSocketNotifier::Write); + QObject::connect(notifier.get(), SIGNAL(activated(int)), impl.get(), SLOT(onWriteEvent(int))); + impl->writePoll[fd] = WatchPair(std::move(notifier), std::move(cb)); + } +} + +void RunLoop::removeWatch(int fd) { + MBGL_VERIFY_THREAD(tid); + + auto writePollIter = impl->writePoll.find(fd); + if (writePollIter != impl->writePoll.end()) { + impl->writePoll.erase(writePollIter); + } + + auto readPollIter = impl->readPoll.find(fd); + if (readPollIter != impl->readPoll.end()) { + impl->readPoll.erase(readPollIter); + } +} + +} +} diff --git a/platform/qt/src/run_loop_impl.hpp b/platform/qt/src/run_loop_impl.hpp new file mode 100644 index 0000000000..6cabbbb425 --- /dev/null +++ b/platform/qt/src/run_loop_impl.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include <mbgl/platform/log.hpp> +#include <mbgl/util/async_task.hpp> +#include <mbgl/util/run_loop.hpp> + +#include <QEventLoop> +#include <QObject> +#include <QSocketNotifier> + +#include <unordered_map> + +namespace mbgl { +namespace util { + +using WatchCallback = std::function<void(int, RunLoop::Event)>; +using WatchPair = std::pair<std::unique_ptr<QSocketNotifier>, WatchCallback>; + +class RunLoop::Impl : public QObject { + Q_OBJECT + +public: + Impl() = default; + + RunLoop::Type type; + + std::unique_ptr<QEventLoop> loop; + std::unique_ptr<AsyncTask> async; + + std::unordered_map<int, WatchPair> readPoll; + std::unordered_map<int, WatchPair> writePoll; + +public slots: + void onReadEvent(int fd); + void onWriteEvent(int fd); +}; + +} +} diff --git a/platform/qt/src/timer.cpp b/platform/qt/src/timer.cpp new file mode 100644 index 0000000000..6d0ccb41d7 --- /dev/null +++ b/platform/qt/src/timer.cpp @@ -0,0 +1,57 @@ +#include <mbgl/util/timer.hpp> + +#include <mbgl/util/run_loop.hpp> + +#include <memory> + +#include "timer_impl.hpp" + +namespace mbgl { +namespace util { + +Timer::Impl::Impl() { +#if QT_VERSION >= 0x050000 + timer.setTimerType(Qt::PreciseTimer); +#endif + connect(&timer, SIGNAL(timeout()), this, SLOT(timerFired())); +} + +void Timer::Impl::start(uint64_t timeout, uint64_t repeat_, std::function<void ()>&& cb) { + repeat = repeat_; + callback = std::move(cb); + + timer.setSingleShot(true); + timer.start(timeout); +} + +void Timer::Impl::stop() { + timer.stop(); +} + +void Timer::Impl::timerFired() { + if (repeat) { + timer.setSingleShot(false); + timer.start(repeat); + } + + callback(); +} + +Timer::Timer() + : impl(std::make_unique<Impl>()) { +} + +Timer::~Timer() = default; + +void Timer::start(Duration timeout, Duration repeat, std::function<void()>&& cb) { + impl->start(std::chrono::duration_cast<std::chrono::milliseconds>(timeout).count(), + std::chrono::duration_cast<std::chrono::milliseconds>(repeat).count(), + std::move(cb)); +} + +void Timer::stop() { + impl->stop(); +} + +} +} diff --git a/platform/qt/src/timer_impl.hpp b/platform/qt/src/timer_impl.hpp new file mode 100644 index 0000000000..a54abfd8b5 --- /dev/null +++ b/platform/qt/src/timer_impl.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include <mbgl/util/timer.hpp> + +#include <QObject> +#include <QTimer> + +namespace mbgl { +namespace util { + +class Timer::Impl : public QObject { + Q_OBJECT + +public: + Impl(); + + void start(uint64_t timeout, uint64_t repeat, std::function<void ()>&& cb); + void stop(); + +public slots: + void timerFired(); + +private: + uint64_t repeat; + std::function<void()> callback; + + QTimer timer; +}; + +} +} |