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