1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
4 // Use of this source code is governed by a BSD-style license that can be
5 // found in the LICENSE file.
6 
7 #include <qabstracteventdispatcher.h>
8 #include <qevent.h>
9 #include <QCoreApplication>
10 #include <QThread>
11 #include <qtimer.h>
12 
13 #include "base/message_pump_qt.h"
14 
15 #include <fcntl.h>
16 #include <limits>
17 #include <math.h>
18 
19 #include "base/eintr_wrapper.h"
20 #include "base/logging.h"
21 #include "base/platform_thread.h"
22 
23 namespace {
24 // Cached QEvent user type, registered for our event system
25 static int sPokeEvent;
26 }  // namespace
27 
28 namespace base {
29 
MessagePumpForUI()30 MessagePumpForUI::MessagePumpForUI() : state_(NULL), qt_pump(*this) {}
31 
~MessagePumpForUI()32 MessagePumpForUI::~MessagePumpForUI() {}
33 
MessagePumpQt(MessagePumpForUI & aPump)34 MessagePumpQt::MessagePumpQt(MessagePumpForUI& aPump)
35     : pump(aPump), mTimer(new QTimer(this)) {
36   // Register our custom event type, to use in qApp event loop
37   sPokeEvent = QEvent::registerEventType();
38   connect(mTimer, SIGNAL(timeout()), this, SLOT(dispatchDelayed()));
39   mTimer->setSingleShot(true);
40 }
41 
~MessagePumpQt()42 MessagePumpQt::~MessagePumpQt() {
43   mTimer->stop();
44   delete mTimer;
45 }
46 
event(QEvent * e)47 bool MessagePumpQt::event(QEvent* e) {
48   if (e->type() == sPokeEvent) {
49     pump.HandleDispatch();
50     return true;
51   }
52   return false;
53 }
54 
scheduleDelayedIfNeeded(const TimeTicks & delayed_work_time)55 void MessagePumpQt::scheduleDelayedIfNeeded(
56     const TimeTicks& delayed_work_time) {
57   if (delayed_work_time.is_null()) {
58     return;
59   }
60 
61   if (mTimer->isActive()) {
62     mTimer->stop();
63   }
64 
65   TimeDelta later = delayed_work_time - TimeTicks::Now();
66   // later.InMilliseconds() returns an int64_t, QTimer only accepts int's for
67   // start(), std::min only works on exact same types.
68   int laterMsecs = later.InMilliseconds() > std::numeric_limits<int>::max()
69                        ? std::numeric_limits<int>::max()
70                        : later.InMilliseconds();
71   mTimer->start(laterMsecs > 0 ? laterMsecs : 0);
72 }
73 
dispatchDelayed()74 void MessagePumpQt::dispatchDelayed() { pump.HandleDispatch(); }
75 
Run(Delegate * delegate)76 void MessagePumpForUI::Run(Delegate* delegate) {
77   RunState state;
78   state.delegate = delegate;
79   state.should_quit = false;
80   state.run_depth = state_ ? state_->run_depth + 1 : 1;
81   // We really only do a single task for each iteration of the loop.  If we
82   // have done something, assume there is likely something more to do.  This
83   // will mean that we don't block on the message pump until there was nothing
84   // more to do.  We also set this to true to make sure not to block on the
85   // first iteration of the loop, so RunAllPending() works correctly.
86   bool more_work_is_plausible = true;
87 
88   RunState* previous_state = state_;
89   state_ = &state;
90 
91   for (;;) {
92     QEventLoop::ProcessEventsFlags block = QEventLoop::AllEvents;
93     if (!more_work_is_plausible) {
94       block |= QEventLoop::WaitForMoreEvents;
95     }
96 
97     QAbstractEventDispatcher* dispatcher =
98         QAbstractEventDispatcher::instance(QThread::currentThread());
99     // An assertion seems too much here, as during startup,
100     // the dispatcher might not be ready yet.
101     if (!dispatcher) {
102       return;
103     }
104 
105     // processEvent's returns true if an event has been processed.
106     more_work_is_plausible = dispatcher->processEvents(block);
107 
108     if (state_->should_quit) {
109       break;
110     }
111 
112     more_work_is_plausible |= state_->delegate->DoWork();
113     if (state_->should_quit) {
114       break;
115     }
116 
117     more_work_is_plausible |=
118         state_->delegate->DoDelayedWork(&delayed_work_time_);
119     if (state_->should_quit) {
120       break;
121     }
122 
123     qt_pump.scheduleDelayedIfNeeded(delayed_work_time_);
124 
125     if (more_work_is_plausible) {
126       continue;
127     }
128 
129     more_work_is_plausible = state_->delegate->DoIdleWork();
130     if (state_->should_quit) {
131       break;
132     }
133   }
134 
135   state_ = previous_state;
136 }
137 
HandleDispatch()138 void MessagePumpForUI::HandleDispatch() {
139   if (state_->should_quit) {
140     return;
141   }
142 
143   if (state_->delegate->DoWork()) {
144     // there might be more, see more_work_is_plausible
145     // variable above, that's why we ScheduleWork() to keep going.
146     ScheduleWork();
147   }
148 
149   if (state_->should_quit) {
150     return;
151   }
152 
153   state_->delegate->DoDelayedWork(&delayed_work_time_);
154   qt_pump.scheduleDelayedIfNeeded(delayed_work_time_);
155 }
156 
Quit()157 void MessagePumpForUI::Quit() {
158   if (state_) {
159     state_->should_quit = true;
160   } else {
161     NOTREACHED() << "Quit called outside Run!";
162   }
163 }
164 
ScheduleWork()165 void MessagePumpForUI::ScheduleWork() {
166   QCoreApplication::postEvent(&qt_pump, new QEvent((QEvent::Type)sPokeEvent));
167 }
168 
ScheduleDelayedWork(const TimeTicks & delayed_work_time)169 void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) {
170   // On GLib implementation, a work source is defined which explicitly checks
171   // the time that has passed. Here, on Qt we can use a QTimer that enqueues our
172   // event signal in an event queue.
173   delayed_work_time_ = delayed_work_time;
174   qt_pump.scheduleDelayedIfNeeded(delayed_work_time_);
175 }
176 
177 }  // namespace base
178