Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
.
[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 terminateThread;
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     bool synced;
160     d->safeLock(d->syncMutex);
161     if (d->terminateThread) {
162         synced = false;
163     } else {
164         qApp->postEvent(this, new QEvent(QEvent::User));
165         synced = d->syncCondition.wait(&d->syncMutex, time);
166     }
167     d->safeUnlock(d->syncMutex);
168     return synced;
169 }
170
171 void DrawingWindow::sleep(unsigned long secs)
172 {
173     DrawingThread::sleep(secs);
174 }
175
176 void DrawingWindow::msleep(unsigned long msecs)
177 {
178     DrawingThread::msleep(msecs);
179 }
180
181 void DrawingWindow::usleep(unsigned long usecs)
182 {
183     DrawingThread::usleep(usecs);
184 }
185
186 void DrawingWindow::closeEvent(QCloseEvent *ev)
187 {
188     d->timer.stop();
189     d->thread->terminate();
190     d->syncMutex.lock();
191     d->terminateThread = true;  // this flag is needed for the case
192                                 // where the following wakeAll() call
193                                 // occurs between the
194                                 // setTerminationEnable(false) and the
195                                 // mutex lock in safeLock() called
196                                 // from sync()
197     d->syncCondition.wakeAll();
198     d->syncMutex.unlock();
199     QWidget::closeEvent(ev);
200     d->thread->wait();
201 }
202
203 void DrawingWindow::customEvent(QEvent *)
204 {
205     d->update();
206     qApp->sendPostedEvents(this, QEvent::UpdateLater);
207     qApp->sendPostedEvents(this, QEvent::UpdateRequest);
208     qApp->sendPostedEvents(this, QEvent::Paint);
209     qApp->processEvents(QEventLoop::ExcludeUserInputEvents |
210                         QEventLoop::ExcludeSocketNotifiers |
211                         QEventLoop::DeferredDeletion |
212                         QEventLoop::X11ExcludeTimers);
213     qApp->flush();
214     qApp->syncX();
215     d->syncMutex.lock();
216     d->syncCondition.wakeAll();
217     d->syncMutex.unlock();
218 }
219
220 void DrawingWindow::keyPressEvent(QKeyEvent *ev)
221 {
222     bool accept = true;
223     switch (ev->key()) {
224     case Qt::Key_Escape:
225         close();
226         break;
227     default:
228         accept = false;
229         break;
230     }
231     if (accept)
232         ev->accept();
233 }
234
235 void DrawingWindow::paintEvent(QPaintEvent *ev)
236 {
237     QPainter widgetPainter(this);
238     d->imageMutex.lock();
239     QImage imageCopy(*d->image);
240     d->imageMutex.unlock();
241     QRect rect = ev->rect();
242     widgetPainter.drawImage(rect, imageCopy, rect);
243 }
244
245 void DrawingWindow::showEvent(QShowEvent *ev)
246 {
247     d->timer.start(d->paintInterval, this);
248     d->thread->start_once(QThread::IdlePriority);
249     QWidget::showEvent(ev);
250 }
251
252 void DrawingWindow::timerEvent(QTimerEvent *ev)
253 {
254     if (ev->timerId() == d->timer.timerId()) {
255         d->update();
256         d->timer.start(d->paintInterval, this);
257     } else {
258         QWidget::timerEvent(ev);
259     }
260 }
261
262 //--- DrawingWindowPrivate ---------------------------------------------
263
264 DrawingWindowPrivate::DrawingWindowPrivate(DrawingWindow *w,
265                                            DrawingWindow::ThreadFunction f)
266     : q(w)
267     , terminateThread(false)
268     , lockCount(0)
269     , image(new QImage(q->width, q->height, QImage::Format_RGB32))
270     , painter(new QPainter(image))
271     , thread(new DrawingThread(*q, f))
272 {
273 }
274
275 void DrawingWindowPrivate::initialize()
276 {
277     q->setFocusPolicy(Qt::StrongFocus);
278     q->setFixedSize(image->size());
279     q->setAttribute(Qt::WA_OpaquePaintEvent);
280     q->setFocus();
281
282     q->setColor(0.0, 0.0, 0.0); // black
283     q->setBgColor(1.0, 1.0, 1.0); // white
284     q->clearGraph();
285
286     dirtyFlag = false;
287 }
288
289 DrawingWindowPrivate::~DrawingWindowPrivate()
290 {
291     delete thread;
292     delete painter;
293     delete image;
294 }
295
296 inline
297 void DrawingWindowPrivate::safeLock(QMutex &mutex)
298 {
299     if (lockCount++ == 0)
300         thread->setTerminationEnabled(false);
301     mutex.lock();
302 }
303
304 inline
305 void DrawingWindowPrivate::safeUnlock(QMutex &mutex)
306 {
307     mutex.unlock();
308     if (--lockCount == 0)
309         thread->setTerminationEnabled(true);
310 }
311
312 inline
313 void DrawingWindowPrivate::dirty()
314 {
315     dirtyFlag = true;
316     dirtyRect = image->rect();
317 }
318
319 inline
320 void DrawingWindowPrivate::dirty(int x, int y)
321 {
322     dirty(QRect(x, y, 1, 1));
323 }
324
325 inline
326 void DrawingWindowPrivate::dirty(int x1, int y1, int x2, int y2)
327 {
328     QRect r;
329     r.setCoords(x1, y1, x2, y2);
330     dirty(r.normalized());
331 }
332
333 void DrawingWindowPrivate::dirty(const QRect &rect)
334 {
335     if (dirtyFlag) {
336         dirtyRect |= rect;
337     } else {
338         dirtyFlag = true;
339         dirtyRect = rect;
340     }
341 }
342
343 void DrawingWindowPrivate::update()
344 {
345     imageMutex.lock();
346     bool dirty = dirtyFlag;
347     QRect rect = dirtyRect;
348     dirtyFlag = false;
349     imageMutex.unlock();
350     if (dirty)
351         q->update(rect);
352 }
353
354 //--- DrawingThread ----------------------------------------------------
355
356 DrawingThread::DrawingThread(DrawingWindow &w, DrawingWindow::ThreadFunction f)
357     : drawingWindow(w)
358     , threadFunction(f)
359     , started_once(false)
360 {
361 }
362
363 void DrawingThread::start_once(Priority priority)
364 {
365     if (!started_once) {
366         started_once = true;
367         start(priority);
368     }
369 }
370
371 void DrawingThread::run()
372 {
373     threadFunction(drawingWindow);
374 }