michael@0: // Copyright (c) 2010 The Chromium Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "base/message_pump_qt.h" michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "base/eintr_wrapper.h" michael@0: #include "base/lazy_instance.h" michael@0: #include "base/logging.h" michael@0: #include "base/platform_thread.h" michael@0: michael@0: namespace { michael@0: // Cached QEvent user type, registered for our event system michael@0: static int sPokeEvent; michael@0: } // namespace michael@0: michael@0: namespace base { michael@0: michael@0: MessagePumpForUI::MessagePumpForUI() michael@0: : state_(NULL), michael@0: qt_pump(*this) michael@0: { michael@0: } michael@0: michael@0: MessagePumpForUI::~MessagePumpForUI() { michael@0: } michael@0: michael@0: MessagePumpQt::MessagePumpQt(MessagePumpForUI &aPump) michael@0: : pump(aPump), mTimer(new QTimer(this)) michael@0: { michael@0: // Register our custom event type, to use in qApp event loop michael@0: sPokeEvent = QEvent::registerEventType(); michael@0: connect(mTimer, SIGNAL(timeout()), this, SLOT(dispatchDelayed())); michael@0: mTimer->setSingleShot(true); michael@0: } michael@0: michael@0: MessagePumpQt::~MessagePumpQt() michael@0: { michael@0: mTimer->stop(); michael@0: delete mTimer; michael@0: } michael@0: michael@0: bool michael@0: MessagePumpQt::event(QEvent *e) michael@0: { michael@0: if (e->type() == sPokeEvent) { michael@0: pump.HandleDispatch(); michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: MessagePumpQt::scheduleDelayedIfNeeded(const TimeTicks& delayed_work_time) michael@0: { michael@0: if (delayed_work_time.is_null()) { michael@0: return; michael@0: } michael@0: michael@0: if (mTimer->isActive()) { michael@0: mTimer->stop(); michael@0: } michael@0: michael@0: TimeDelta later = delayed_work_time - TimeTicks::Now(); michael@0: // later.InMilliseconds() returns an int64_t, QTimer only accepts int's for start(), michael@0: // std::min only works on exact same types. michael@0: int laterMsecs = later.InMilliseconds() > std::numeric_limits::max() ? michael@0: std::numeric_limits::max() : later.InMilliseconds(); michael@0: mTimer->start(laterMsecs > 0 ? laterMsecs : 0); michael@0: } michael@0: michael@0: void michael@0: MessagePumpQt::dispatchDelayed() michael@0: { michael@0: pump.HandleDispatch(); michael@0: } michael@0: michael@0: void MessagePumpForUI::Run(Delegate* delegate) { michael@0: RunState state; michael@0: state.delegate = delegate; michael@0: state.should_quit = false; michael@0: state.run_depth = state_ ? state_->run_depth + 1 : 1; michael@0: // We really only do a single task for each iteration of the loop. If we michael@0: // have done something, assume there is likely something more to do. This michael@0: // will mean that we don't block on the message pump until there was nothing michael@0: // more to do. We also set this to true to make sure not to block on the michael@0: // first iteration of the loop, so RunAllPending() works correctly. michael@0: bool more_work_is_plausible = true; michael@0: michael@0: RunState* previous_state = state_; michael@0: state_ = &state; michael@0: michael@0: for(;;) { michael@0: QEventLoop::ProcessEventsFlags block = QEventLoop::AllEvents; michael@0: if (!more_work_is_plausible) { michael@0: block |= QEventLoop::WaitForMoreEvents; michael@0: } michael@0: michael@0: QAbstractEventDispatcher* dispatcher = michael@0: QAbstractEventDispatcher::instance(QThread::currentThread()); michael@0: // An assertion seems too much here, as during startup, michael@0: // the dispatcher might not be ready yet. michael@0: if (!dispatcher) { michael@0: return; michael@0: } michael@0: michael@0: // processEvent's returns true if an event has been processed. michael@0: more_work_is_plausible = dispatcher->processEvents(block); michael@0: michael@0: if (state_->should_quit) { michael@0: break; michael@0: } michael@0: michael@0: more_work_is_plausible |= state_->delegate->DoWork(); michael@0: if (state_->should_quit) { michael@0: break; michael@0: } michael@0: michael@0: more_work_is_plausible |= michael@0: state_->delegate->DoDelayedWork(&delayed_work_time_); michael@0: if (state_->should_quit) { michael@0: break; michael@0: } michael@0: michael@0: qt_pump.scheduleDelayedIfNeeded(delayed_work_time_); michael@0: michael@0: if (more_work_is_plausible) { michael@0: continue; michael@0: } michael@0: michael@0: more_work_is_plausible = state_->delegate->DoIdleWork(); michael@0: if (state_->should_quit) { michael@0: break; michael@0: } michael@0: } michael@0: michael@0: state_ = previous_state; michael@0: } michael@0: michael@0: void MessagePumpForUI::HandleDispatch() { michael@0: if (state_->should_quit) { michael@0: return; michael@0: } michael@0: michael@0: if (state_->delegate->DoWork()) { michael@0: // there might be more, see more_work_is_plausible michael@0: // variable above, that's why we ScheduleWork() to keep going. michael@0: ScheduleWork(); michael@0: } michael@0: michael@0: if (state_->should_quit) { michael@0: return; michael@0: } michael@0: michael@0: state_->delegate->DoDelayedWork(&delayed_work_time_); michael@0: qt_pump.scheduleDelayedIfNeeded(delayed_work_time_); michael@0: } michael@0: michael@0: void MessagePumpForUI::Quit() { michael@0: if (state_) { michael@0: state_->should_quit = true; michael@0: } else { michael@0: NOTREACHED() << "Quit called outside Run!"; michael@0: } michael@0: } michael@0: michael@0: void MessagePumpForUI::ScheduleWork() { michael@0: QCoreApplication::postEvent(&qt_pump, michael@0: new QEvent((QEvent::Type) sPokeEvent)); michael@0: } michael@0: michael@0: void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { michael@0: // On GLib implementation, a work source is defined which explicitly checks the michael@0: // time that has passed. Here, on Qt we can use a QTimer that enqueues our michael@0: // event signal in an event queue. michael@0: delayed_work_time_ = delayed_work_time; michael@0: qt_pump.scheduleDelayedIfNeeded(delayed_work_time_); michael@0: } michael@0: michael@0: } // namespace base