Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
eb79aec399edd39eb4eb7182b5c036a5f10ce20f
[graphlib.git] / DrawingWindow.cpp
1 #include "DrawingWindow.h"
2 #include <QApplication>
3 #include <QBasicTimer>
4 #include <QColor>
5 #include <QImage>
6 #include <QMutex>
7 #include <QPaintEvent>
8 #include <QPainter>
9 #include <QRect>
10 #include <QThread>
11 #include <QTimerEvent>
12 #include <QWaitCondition>
13
14 class DrawingThread: public QThread {
15 public:
16     DrawingThread(DrawingWindow &w, DrawingWindow::ThreadFunction f);
17     void start_once(Priority priority = InheritPriority);
18
19 protected:
20     void run();
21
22 private:
23     DrawingWindow &drawingWindow;
24     DrawingWindow::ThreadFunction threadFunction;
25     bool started_once;
26
27     friend class DrawingWindow;
28     friend class DrawingWindowPrivate;
29 };
30
31 class DrawingWindowPrivate {
32 public:
33     static const int paintInterval = 33;
34
35     DrawingWindow * const q;
36
37     QBasicTimer timer;
38     QMutex imageMutex;
39     QMutex syncMutex;
40     QWaitCondition syncCondition;
41     bool painted;
42     int lockCount;
43
44     QImage *image;
45     QPainter *painter;
46
47     QColor fgColor;
48     QColor bgColor;
49
50     bool dirtyFlag;
51     QRect dirtyRect;
52
53     DrawingThread *thread;
54
55     DrawingWindowPrivate(DrawingWindow *w,
56                          DrawingWindow::ThreadFunction f);
57     ~DrawingWindowPrivate();
58
59     void initialize();
60
61     void safeLock(QMutex &mutex);
62     void safeUnlock(QMutex &mutex);
63
64     void dirty();
65     void dirty(int x, int y);
66     void dirty(int x1, int y1, int x2, int y2);
67     void dirty(const QRect &rect);
68
69     void update();
70 };
71
72 //--- DrawingWindow ----------------------------------------------------
73
74 DrawingWindow::DrawingWindow(ThreadFunction f, int w, int h)
75     : QWidget()
76     , width(w)
77     , height(h)
78     , d(new DrawingWindowPrivate(this, f))
79 {
80     d->initialize();
81 }
82
83 DrawingWindow::DrawingWindow(QWidget *parent,
84                              ThreadFunction f, int w, int h)
85     : QWidget(parent)
86     , width(w)
87     , height(h)
88     , d(new DrawingWindowPrivate(this, f))
89 {
90     d->initialize();
91 }
92
93 DrawingWindow::DrawingWindow(QWidget *parent, Qt::WindowFlags flags,
94                              ThreadFunction f, int w, int h)
95     : QWidget(parent, flags)
96     , width(w)
97     , height(h)
98     , d(new DrawingWindowPrivate(this, f))
99 {
100     d->initialize();
101 }
102
103 DrawingWindow::~DrawingWindow()
104 {
105     delete d;
106 }
107
108 void DrawingWindow::setColor(float red, float green, float blue)
109 {
110     d->fgColor.setRgbF(red, green, blue);
111     QPen pen(d->painter->pen());
112     pen.setColor(d->fgColor);
113     d->painter->setPen(pen);
114 }
115
116 void DrawingWindow::setBgColor(float red, float green, float blue)
117 {
118     d->bgColor.setRgbF(red, green, blue);
119 }
120
121 void DrawingWindow::clearGraph()
122 {
123     d->safeLock(d->imageMutex);
124     d->painter->fillRect(d->image->rect(), d->bgColor);    
125     d->dirty();
126     d->safeUnlock(d->imageMutex);
127 }
128
129 void DrawingWindow::drawPoint(int x, int y)
130 {
131     d->safeLock(d->imageMutex);
132     d->painter->drawPoint(x, y);
133     d->dirty(x, y);
134     d->safeUnlock(d->imageMutex);
135 }
136
137 void DrawingWindow::drawLine(int x1, int y1, int x2, int y2)
138 {
139     d->safeLock(d->imageMutex);
140     d->painter->drawLine(x1, y1, x2, y2);
141     d->dirty(x1, y1, x2, y2);
142     d->safeUnlock(d->imageMutex);
143 }
144
145 void DrawingWindow::drawRect(int x1, int y1, int x2, int y2)
146 {
147     QRect r;
148     r.setCoords(x1, y1, x2, y2);
149     r = r.normalized();
150     d->safeLock(d->imageMutex);
151     d->painter->drawRect(r);
152     r.adjust(0, 0, 1, 1);
153     d->dirty(r);
154     d->safeUnlock(d->imageMutex);
155 }
156
157 bool DrawingWindow::sync(unsigned long time)
158 {
159     d->safeLock(d->syncMutex);
160     qApp->postEvent(this, new QEvent(QEvent::User));
161     bool synced = d->syncCondition.wait(&d->syncMutex, time);
162     d->safeUnlock(d->syncMutex);
163     return synced;
164 }
165
166 void DrawingWindow::sleep(unsigned long secs)
167 {
168     DrawingThread::sleep(secs);
169 }
170
171 void DrawingWindow::msleep(unsigned long msecs)
172 {
173     DrawingThread::msleep(msecs);
174 }
175
176 void DrawingWindow::usleep(unsigned long usecs)
177 {
178     DrawingThread::usleep(usecs);
179 }
180
181 void DrawingWindow::closeEvent(QCloseEvent *ev)
182 {
183     d->timer.stop();
184     d->thread->terminate();
185     d->syncMutex.lock();
186     d->syncCondition.wakeAll();
187     d->syncMutex.unlock();
188     QWidget::closeEvent(ev);
189     d->thread->wait();
190 }
191
192 void DrawingWindow::customEvent(QEvent *)
193 {
194     d->syncMutex.lock();
195     d->update();
196     qApp->sendPostedEvents(this, QEvent::UpdateLater);
197     qApp->sendPostedEvents(this, QEvent::UpdateRequest);
198     qApp->sendPostedEvents(this, QEvent::Paint);
199     if (0) {                    // Disabled because this can lead to
200                                 // dead-lock if a close event is
201                                 // processed !
202         qApp->processEvents(QEventLoop::ExcludeUserInputEvents |
203                             QEventLoop::ExcludeSocketNotifiers |
204                             QEventLoop::DeferredDeletion |
205                             QEventLoop::X11ExcludeTimers);
206     }
207     qApp->flush();
208     qApp->syncX();
209     d->syncCondition.wakeAll();
210     d->syncMutex.unlock();
211 }
212
213 void DrawingWindow::keyPressEvent(QKeyEvent *ev)
214 {
215     bool accept = true;
216     switch (ev->key()) {
217     case Qt::Key_Escape:
218         close();
219         break;
220     default:
221         accept = false;
222         break;
223     }
224     if (accept)
225         ev->accept();
226 }
227
228 void DrawingWindow::paintEvent(QPaintEvent *ev)
229 {
230     QPainter widgetPainter(this);
231     d->imageMutex.lock();
232     QImage imageCopy(*d->image);
233     d->imageMutex.unlock();
234     QRect rect = ev->rect();
235     widgetPainter.drawImage(rect, imageCopy, rect);
236 }
237
238 void DrawingWindow::showEvent(QShowEvent *ev)
239 {
240     d->timer.start(d->paintInterval, this);
241     d->thread->start_once(QThread::IdlePriority);
242     QWidget::showEvent(ev);
243 }
244
245 void DrawingWindow::timerEvent(QTimerEvent *ev)
246 {
247     if (ev->timerId() == d->timer.timerId()) {
248         d->update();
249         d->timer.start(d->paintInterval, this);
250     } else {
251         QWidget::timerEvent(ev);
252     }
253 }
254
255 //--- DrawingWindowPrivate ---------------------------------------------
256
257 DrawingWindowPrivate::DrawingWindowPrivate(DrawingWindow *w,
258                                            DrawingWindow::ThreadFunction f)
259     : q(w)
260     , lockCount(0)
261     , image(new QImage(q->width, q->height, QImage::Format_RGB32))
262     , painter(new QPainter(image))
263     , thread(new DrawingThread(*q, f))
264 {
265 }
266
267 void DrawingWindowPrivate::initialize()
268 {
269     q->setFocusPolicy(Qt::StrongFocus);
270     q->setFixedSize(image->size());
271     q->setAttribute(Qt::WA_OpaquePaintEvent);
272     q->setFocus();
273
274     q->setColor(0.0, 0.0, 0.0); // black
275     q->setBgColor(1.0, 1.0, 1.0); // white
276     q->clearGraph();
277
278     dirtyFlag = false;
279 }
280
281 DrawingWindowPrivate::~DrawingWindowPrivate()
282 {
283     delete thread;
284     delete painter;
285     delete image;
286 }
287
288 inline
289 void DrawingWindowPrivate::safeLock(QMutex &mutex)
290 {
291     if (lockCount++ == 0)
292         thread->setTerminationEnabled(false);
293     mutex.lock();
294 }
295
296 inline
297 void DrawingWindowPrivate::safeUnlock(QMutex &mutex)
298 {
299     mutex.unlock();
300     if (--lockCount == 0)
301         thread->setTerminationEnabled(true);
302 }
303
304 inline
305 void DrawingWindowPrivate::dirty()
306 {
307     dirtyFlag = true;
308     dirtyRect = image->rect();
309 }
310
311 inline
312 void DrawingWindowPrivate::dirty(int x, int y)
313 {
314     dirty(QRect(x, y, 1, 1));
315 }
316
317 inline
318 void DrawingWindowPrivate::dirty(int x1, int y1, int x2, int y2)
319 {
320     QRect r;
321     r.setCoords(x1, y1, x2, y2);
322     dirty(r.normalized());
323 }
324
325 void DrawingWindowPrivate::dirty(const QRect &rect)
326 {
327     if (dirtyFlag) {
328         dirtyRect |= rect;
329     } else {
330         dirtyFlag = true;
331         dirtyRect = rect;
332     }
333 }
334
335 void DrawingWindowPrivate::update()
336 {
337     imageMutex.lock();
338     bool dirty = dirtyFlag;
339     QRect rect = dirtyRect;
340     dirtyFlag = false;
341     imageMutex.unlock();
342     if (dirty)
343         q->update(rect);
344 }
345
346 //--- DrawingThread ----------------------------------------------------
347
348 DrawingThread::DrawingThread(DrawingWindow &w, DrawingWindow::ThreadFunction f)
349     : drawingWindow(w)
350     , threadFunction(f)
351     , started_once(false)
352 {
353 }
354
355 void DrawingThread::start_once(Priority priority)
356 {
357     if (!started_once) {
358         started_once = true;
359         start(priority);
360     }
361 }
362
363 void DrawingThread::run()
364 {
365     threadFunction(drawingWindow);
366 }