Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
.
[graphlib.git] / DrawingWindow.cpp
index 4e34d9e..fb3561d 100644 (file)
 #include "DrawingWindow.h"
+#include <QApplication>
+#include <QBasicTimer>
+#include <QColor>
+#include <QImage>
+#include <QMutex>
 #include <QPaintEvent>
+#include <QPainter>
+#include <QRect>
 #include <QThread>
 #include <QTimerEvent>
+#include <QWaitCondition>
 
-#include <iostream>
-
-class DrawingWindow::DrawingThread: public QThread {
+class DrawingThread: public QThread {
 public:
-    DrawingThread(DrawingWindow &w, ThreadFunction f)
-        : drawingWindow(w)
-        , threadFunction(f)
-    {
-    }
+    DrawingThread(DrawingWindow &w, DrawingWindow::ThreadFunction f);
+    void start_once(Priority priority = InheritPriority);
 
-    void run()
-    {
-        threadFunction(drawingWindow);
-    }
+protected:
+    void run();
 
 private:
     DrawingWindow &drawingWindow;
-    ThreadFunction threadFunction;
+    DrawingWindow::ThreadFunction threadFunction;
+    bool started_once;
+
+    friend class DrawingWindow;
+    friend class DrawingWindowPrivate;
+};
+
+class DrawingWindowPrivate {
+public:
+    static const int paintInterval = 33;
+
+    DrawingWindow * const q;
+
+    QBasicTimer timer;
+    QMutex imageMutex;
+    QMutex syncMutex;
+    QWaitCondition syncCondition;
+    bool terminateThread;
+    int lockCount;
+
+    QImage *image;
+    QPainter *painter;
+
+    QColor fgColor;
+    QColor bgColor;
+
+    bool dirtyFlag;
+    QRect dirtyRect;
+
+    DrawingThread *thread;
 
+    DrawingWindowPrivate(DrawingWindow *w,
+                         DrawingWindow::ThreadFunction f);
+    ~DrawingWindowPrivate();
+
+    void initialize();
+
+    void safeLock(QMutex &mutex);
+    void safeUnlock(QMutex &mutex);
+
+    void dirty();
+    void dirty(int x, int y);
+    void dirty(int x1, int y1, int x2, int y2);
+    void dirty(const QRect &rect);
+
+    void update();
 };
 
-DrawingWindow::DrawingWindow(ThreadFunction fun, int width, int height)
+//--- DrawingWindow ----------------------------------------------------
+
+DrawingWindow::DrawingWindow(ThreadFunction f, int w, int h)
     : QWidget()
+    , width(w)
+    , height(h)
+    , d(new DrawingWindowPrivate(this, f))
 {
-    initialize(fun, width, height);
+    d->initialize();
 }
 
 DrawingWindow::DrawingWindow(QWidget *parent,
-                             ThreadFunction fun, int width, int height)
+                             ThreadFunction f, int w, int h)
     : QWidget(parent)
+    , width(w)
+    , height(h)
+    , d(new DrawingWindowPrivate(this, f))
 {
-    initialize(fun, width, height);
+    d->initialize();
 }
 
 DrawingWindow::DrawingWindow(QWidget *parent, Qt::WindowFlags flags,
-                             ThreadFunction fun, int width, int height)
+                             ThreadFunction f, int w, int h)
     : QWidget(parent, flags)
+    , width(w)
+    , height(h)
+    , d(new DrawingWindowPrivate(this, f))
 {
-    initialize(fun, width, height);
+    d->initialize();
 }
 
-void DrawingWindow::initialize(ThreadFunction fun, int width, int height)
+DrawingWindow::~DrawingWindow()
 {
-    image = new QImage(width, height, QImage::Format_RGB32);
-    image->fill(QColor(Qt::white).rgb());
+    delete d;
+}
 
-    painter = new QPainter(image);
+void DrawingWindow::setColor(float red, float green, float blue)
+{
+    d->fgColor.setRgbF(red, green, blue);
+    QPen pen(d->painter->pen());
+    pen.setColor(d->fgColor);
+    d->painter->setPen(pen);
+}
 
-    dirtyFlag = false;
+void DrawingWindow::setBgColor(float red, float green, float blue)
+{
+    d->bgColor.setRgbF(red, green, blue);
+}
 
-    setFocusPolicy(Qt::StrongFocus);
-    setFixedSize(image->size());
-    setAttribute(Qt::WA_OpaquePaintEvent);
-    setFocus();
-    timer.start(paintInterval, this);
+void DrawingWindow::clearGraph()
+{
+    d->safeLock(d->imageMutex);
+    d->painter->fillRect(d->image->rect(), d->bgColor);    
+    d->dirty();
+    d->safeUnlock(d->imageMutex);
+}
 
-    thread = new DrawingThread(*this, fun);
-    thread_started = false;
+void DrawingWindow::drawPoint(int x, int y)
+{
+    d->safeLock(d->imageMutex);
+    d->painter->drawPoint(x, y);
+    d->dirty(x, y);
+    d->safeUnlock(d->imageMutex);
+}
 
-    mutex_enabled = true;
+void DrawingWindow::drawLine(int x1, int y1, int x2, int y2)
+{
+    d->safeLock(d->imageMutex);
+    d->painter->drawLine(x1, y1, x2, y2);
+    d->dirty(x1, y1, x2, y2);
+    d->safeUnlock(d->imageMutex);
 }
 
-DrawingWindow::~DrawingWindow()
+void DrawingWindow::drawRect(int x1, int y1, int x2, int y2)
 {
-    mutex.lock();
-    mutex_enabled = false;
-    mutex.unlock();
-    std::cerr << "A\n";
-    thread->terminate();    
-    std::cerr << "B\n";
-    thread->wait();
-    std::cerr << "C\n";
-    delete thread;
-    delete painter;
-    delete image;
-    std::cerr << "D\n";
+    QRect r;
+    r.setCoords(x1, y1, x2, y2);
+    r = r.normalized();
+    d->safeLock(d->imageMutex);
+    d->painter->drawRect(r);
+    r.adjust(0, 0, 1, 1);
+    d->dirty(r);
+    d->safeUnlock(d->imageMutex);
 }
 
-void DrawingWindow::setColor(const QColor &color)
+bool DrawingWindow::sync(unsigned long time)
 {
-    QPen pen(painter->pen());
-    pen.setColor(color);
-    painter->setPen(pen);
+    bool synced;
+    d->safeLock(d->syncMutex);
+    if (d->terminateThread) {
+        synced = false;
+    } else {
+        qApp->postEvent(this, new QEvent(QEvent::User));
+        synced = d->syncCondition.wait(&d->syncMutex, time);
+    }
+    d->safeUnlock(d->syncMutex);
+    return synced;
 }
 
-void DrawingWindow::setColor(float red, float green, float blue)
+void DrawingWindow::sleep(unsigned long secs)
 {
-    QColor color;
-    color.setRgbF(red, green, blue);
-    this->setColor(color);
+    DrawingThread::sleep(secs);
 }
 
-void DrawingWindow::drawPoint(int x, int y)
+void DrawingWindow::msleep(unsigned long msecs)
 {
-    lock();
-    painter->drawPoint(x, y);
-    setDirtyRect(x, y);
-    unlock();
+    DrawingThread::msleep(msecs);
 }
 
-void DrawingWindow::drawLine(int x1, int y1, int x2, int y2)
+void DrawingWindow::usleep(unsigned long usecs)
 {
-    lock();
-    painter->drawLine(x1, y1, x2, y2);
-    setDirtyRect(x1, y1, x2, y2);
-    unlock();
+    DrawingThread::usleep(usecs);
+}
+
+void DrawingWindow::closeEvent(QCloseEvent *ev)
+{
+    d->timer.stop();
+    d->thread->terminate();
+    d->syncMutex.lock();
+    d->terminateThread = true;  // this flag is needed for the case
+                                // where the following wakeAll() call
+                                // occurs between the
+                                // setTerminationEnable(false) and the
+                                // mutex lock in safeLock() called
+                                // from sync()
+    d->syncCondition.wakeAll();
+    d->syncMutex.unlock();
+    QWidget::closeEvent(ev);
+    d->thread->wait();
+}
+
+void DrawingWindow::customEvent(QEvent *)
+{
+    d->update();
+    qApp->sendPostedEvents(this, QEvent::UpdateLater);
+    qApp->sendPostedEvents(this, QEvent::UpdateRequest);
+    qApp->sendPostedEvents(this, QEvent::Paint);
+    qApp->processEvents(QEventLoop::ExcludeUserInputEvents |
+                        QEventLoop::ExcludeSocketNotifiers |
+                        QEventLoop::DeferredDeletion |
+                        QEventLoop::X11ExcludeTimers);
+    qApp->flush();
+    qApp->syncX();
+    d->syncMutex.lock();
+    d->syncCondition.wakeAll();
+    d->syncMutex.unlock();
+}
+
+void DrawingWindow::keyPressEvent(QKeyEvent *ev)
+{
+    bool accept = true;
+    switch (ev->key()) {
+    case Qt::Key_Escape:
+        close();
+        break;
+    default:
+        accept = false;
+        break;
+    }
+    if (accept)
+        ev->accept();
 }
 
 void DrawingWindow::paintEvent(QPaintEvent *ev)
 {
     QPainter widgetPainter(this);
+    d->imageMutex.lock();
+    QImage imageCopy(*d->image);
+    d->imageMutex.unlock();
     QRect rect = ev->rect();
-    lock();
-    QImage imageCopy(*image);
-    unlock();
     widgetPainter.drawImage(rect, imageCopy, rect);
 }
 
 void DrawingWindow::showEvent(QShowEvent *ev)
 {
-    if (!thread_started) {
-        thread->start();
-        thread_started = true;
-    }
+    d->timer.start(d->paintInterval, this);
+    d->thread->start_once(QThread::IdlePriority);
     QWidget::showEvent(ev);
 }
 
 void DrawingWindow::timerEvent(QTimerEvent *ev)
 {
-    if (ev->timerId() == timer.timerId()) {
-        lock();
-        if (dirtyFlag) {
-            update(dirtyRect);
-            dirtyFlag = false;
-        }
-        unlock();
-        timer.start(paintInterval, this);
+    if (ev->timerId() == d->timer.timerId()) {
+        d->update();
+        d->timer.start(d->paintInterval, this);
     } else {
         QWidget::timerEvent(ev);
     }
 }
 
-void DrawingWindow::setDirtyRect(const QRect &rect)
+//--- DrawingWindowPrivate ---------------------------------------------
+
+DrawingWindowPrivate::DrawingWindowPrivate(DrawingWindow *w,
+                                           DrawingWindow::ThreadFunction f)
+    : q(w)
+    , terminateThread(false)
+    , lockCount(0)
+    , image(new QImage(q->width, q->height, QImage::Format_RGB32))
+    , painter(new QPainter(image))
+    , thread(new DrawingThread(*q, f))
+{
+}
+
+void DrawingWindowPrivate::initialize()
+{
+    q->setFocusPolicy(Qt::StrongFocus);
+    q->setFixedSize(image->size());
+    q->setAttribute(Qt::WA_OpaquePaintEvent);
+    q->setFocus();
+
+    q->setColor(0.0, 0.0, 0.0); // black
+    q->setBgColor(1.0, 1.0, 1.0); // white
+    q->clearGraph();
+
+    dirtyFlag = false;
+}
+
+DrawingWindowPrivate::~DrawingWindowPrivate()
+{
+    delete thread;
+    delete painter;
+    delete image;
+}
+
+inline
+void DrawingWindowPrivate::safeLock(QMutex &mutex)
+{
+    if (lockCount++ == 0)
+        thread->setTerminationEnabled(false);
+    mutex.lock();
+}
+
+inline
+void DrawingWindowPrivate::safeUnlock(QMutex &mutex)
+{
+    mutex.unlock();
+    if (--lockCount == 0)
+        thread->setTerminationEnabled(true);
+}
+
+inline
+void DrawingWindowPrivate::dirty()
+{
+    dirtyFlag = true;
+    dirtyRect = image->rect();
+}
+
+inline
+void DrawingWindowPrivate::dirty(int x, int y)
+{
+    dirty(QRect(x, y, 1, 1));
+}
+
+inline
+void DrawingWindowPrivate::dirty(int x1, int y1, int x2, int y2)
+{
+    QRect r;
+    r.setCoords(x1, y1, x2, y2);
+    dirty(r.normalized());
+}
+
+void DrawingWindowPrivate::dirty(const QRect &rect)
 {
     if (dirtyFlag) {
         dirtyRect |= rect;
@@ -154,3 +339,36 @@ void DrawingWindow::setDirtyRect(const QRect &rect)
         dirtyRect = rect;
     }
 }
+
+void DrawingWindowPrivate::update()
+{
+    imageMutex.lock();
+    bool dirty = dirtyFlag;
+    QRect rect = dirtyRect;
+    dirtyFlag = false;
+    imageMutex.unlock();
+    if (dirty)
+        q->update(rect);
+}
+
+//--- DrawingThread ----------------------------------------------------
+
+DrawingThread::DrawingThread(DrawingWindow &w, DrawingWindow::ThreadFunction f)
+    : drawingWindow(w)
+    , threadFunction(f)
+    , started_once(false)
+{
+}
+
+void DrawingThread::start_once(Priority priority)
+{
+    if (!started_once) {
+        started_once = true;
+        start(priority);
+    }
+}
+
+void DrawingThread::run()
+{
+    threadFunction(drawingWindow);
+}