Changeset 751 for trunk/src


Ignore:
Timestamp:
Jun 15, 2010, 5:00:46 PM (15 years ago)
Author:
Dmitry A. Kuminov
Message:

gui: OS/2: Exclude children with real HWNDs (a.k.a native widgets) when flushing paint events to make sure they are not painted over by the parent (see #161 for details).

Location:
trunk/src/gui/painting
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/gui/painting/qwindowsurface_pm.cpp

    r746 r751  
    5858#include <qt_os2.h>
    5959#include <qlibrary.h>
     60#include <qobject.h>
     61#include <qevent.h>
    6062
    6163#include "qwindowsurface_pm_p.h"
     
    323325    QPMDiveWindowSurfaceFB(QWidget *widget);
    324326    ~QPMDiveWindowSurfaceFB();
    325     void doFlush(const QRect &from, const QPoint &to);
     327    void doFlush(QWidget *widget, const QRect &from, const QPoint &to);
    326328};
    327329
    328 struct QPMDiveWindowSurfacePrivate
    329 {
     330struct QPMDiveWindowSurfacePrivate : public QObject, private QWidget::PmEventFilter
     331{
     332    QPMDiveWindowSurface *that;
     333
    330334    QImage *image;
    331335    HDIVE hDive;
     
    333337    ULONG bufNum;
    334338    bool posDirty;
    335     bool vrnDirty;
    336339    bool vrnDisabled;
    337340    bool inDrag;
    338     int windowHeight;
    339341    SETUP_BLITTER setup;
    340     QVector<RECTL> rcls;
    341342
    342343    struct FlushArgs
    343344    {
     345        QWidget *widget;
    344346        QRect from;
    345347        QPoint to;
    346348    };
    347349    QList<FlushArgs> pending;
     350
     351    struct WidgetData
     352    {
     353        QWidget *widget;
     354        int widgetHeight;
     355        bool vrnDirty;
     356        size_t rclCount;
     357        QVector<RECTL> rcls;
     358    };
     359
     360    WidgetData data;
     361    QMap<HWND, WidgetData> *subWidgets;
     362
     363    void addWidget(QWidget *widget);
     364    void removeWidget(QWidget *widget);
     365
     366    WidgetData *widgetData(QWidget *widget) {
     367        // check for the most common case (no sub-widgets with own HWNDs)
     368        if (widget == that->window())
     369            return &data;
     370        if (!subWidgets || !subWidgets->contains(widget->winId()))
     371            return 0;
     372        return &(*subWidgets)[widget->winId()];
     373    }
     374
     375    bool eventFilter(QObject *obj, QEvent *event);
     376    bool pmEventFilter(QMSG *msg, MRESULT *result);
    348377};
     378
     379void QPMDiveWindowSurfacePrivate::addWidget(QWidget *widget)
     380{
     381    WidgetData *wd = &data;
     382    if (widget != that->window()) {
     383        // lazily create the sub-widget map (only when really necessary)
     384        if (!subWidgets)
     385            subWidgets = new QMap<HWND, WidgetData>();
     386        wd = &(*subWidgets)[widget->winId()];
     387    }
     388
     389    wd->widget = widget;
     390    wd->vrnDirty = true;
     391    wd->widgetHeight = 0;
     392    wd->rclCount = 0;
     393
     394    if (widget == that->window()) {
     395        // receive visible region change messages (note that we don't do this
     396        // for sub-widgets as it causes some strange PM freezes if these widgets
     397        // are embedded windows manipulated by other processes)
     398        widget->addPmEventFilter(this);
     399        WinSetVisibleRegionNotify(widget->winId(), TRUE);
     400    } else {
     401        // receive reparent/destruction messages from children
     402        // to cleanup the map
     403        widget->installEventFilter(this);
     404    }
     405}
     406
     407void QPMDiveWindowSurfacePrivate::removeWidget(QWidget *widget)
     408{
     409    if (widget == that->window()) {
     410        WinSetVisibleRegionNotify(widget->winId(), FALSE);
     411        widget->removePmEventFilter(this);
     412    } else {
     413        widget->removeEventFilter(this);
     414    }
     415
     416    if (widget != that->window())
     417        subWidgets->remove(widget->winId());
     418}
     419
     420bool QPMDiveWindowSurfacePrivate::eventFilter(QObject *obj, QEvent *event)
     421{
     422    QWidget *widget = qobject_cast<QWidget *>(obj);
     423    if (event->type() == QEvent::ParentAboutToChange ||
     424        event->type() == QEvent::Destroy) {
     425        removeWidget(widget);
     426    }
     427
     428    return false;
     429}
     430
     431bool QPMDiveWindowSurfacePrivate::pmEventFilter(QMSG *msg, MRESULT *result)
     432{
     433    switch (msg->msg) {
     434        case WM_VRNDISABLED: {
     435            if (!useFB)
     436                DiveSetupBlitter(hDive, NULL);
     437            vrnDisabled = true;
     438            *result = 0;
     439            return true;
     440        }
     441        case DM_DRAGOVER: {
     442            inDrag = true;
     443            break;
     444        }
     445        case DM_DRAGLEAVE:
     446        case DM_DROP: {
     447            inDrag = false;
     448            break;
     449        }
     450        case WM_VRNENABLED: {
     451            vrnDisabled = false;
     452            // Note that when an overlapping window of *other* process is moved
     453            // over this window, PM still sends WM_VRNENABLED to it but with
     454            // ffVisRgnChanged set to FALSE although the visible region *does*
     455            // actually change (it doesn't seem to be the case for windows of
     456            // the same process). The solution is to ignore this flag and always
     457            // assume that the visible region was changed when we get this msg
     458#if 0
     459            if (LONGFROMMP(msg->mp1)) // window's visible region changed
     460#endif
     461            {
     462                // mark all widgets' visible regions as dirty
     463                data.vrnDirty = true;
     464                if (subWidgets) {
     465                    foreach(HWND hwnd, subWidgets->keys())
     466                        (*subWidgets)[hwnd].vrnDirty = true;
     467                }
     468            }
     469            posDirty = true;
     470
     471            // process pending flush events
     472            foreach(const QPMDiveWindowSurfacePrivate::FlushArgs &args, pending)
     473                that->doFlush(args.widget, args.from, args.to);
     474            pending.clear();
     475
     476            *result = 0;
     477            return true;
     478        }
     479        default:
     480            break;
     481    }
     482
     483    return false;
     484}
    349485
    350486QPMDiveWindowSurface::QPMDiveWindowSurface(QWidget* widget)
    351487    : QWindowSurface(widget), d(new QPMDiveWindowSurfacePrivate)
    352488{
     489    d->that = this;
    353490    d->image = 0;
    354491    d->hDive = NULLHANDLE;
     
    356493    d->bufNum = 0;
    357494    d->posDirty = true;
    358     d->vrnDirty = true;
    359495    d->vrnDisabled = false;
    360496    d->inDrag = false;
    361     d->windowHeight = 0;
     497    d->subWidgets = 0;
    362498
    363499    memset(&d->setup, 0, sizeof(SETUP_BLITTER));
     
    369505    d->setup.fccDstColorFormat = FOURCC_SCRN;
    370506
    371     window()->addPmEventFilter(this);
    372     WinSetVisibleRegionNotify(window()->winId(), TRUE);
     507    // add self to the map of participating widgets
     508    d->addWidget(window());
    373509
    374510    setStaticContentsSupport(true);
     
    377513QPMDiveWindowSurface::~QPMDiveWindowSurface()
    378514{
    379     WinSetVisibleRegionNotify(window()->winId(), FALSE);
    380     window()->removePmEventFilter(this);
     515    if (d->subWidgets) {
     516        foreach(HWND hwnd, d->subWidgets->keys())
     517            d->removeWidget((*d->subWidgets)[hwnd].widget);
     518        Q_ASSERT(d->subWidgets->count() == 0);
     519        delete d->subWidgets;
     520    }
     521
     522    d->removeWidget(window());
    381523
    382524    if (d->bufNum)
     
    400542    if (!d->image || rgn.rectCount() == 0)
    401543        return;
     544
     545    // make sure the widget is known to us
     546    if (!d->widgetData(widget))
     547        d->addWidget(widget);
    402548
    403549    QRect br = rgn.boundingRect();
     
    465611    if (d->vrnDisabled) {
    466612        // defer the flush
    467         QPMDiveWindowSurfacePrivate::FlushArgs args = { br, wbr.topLeft() };
     613        QPMDiveWindowSurfacePrivate::FlushArgs args = { widget,
     614                                                        br, wbr.topLeft() };
    468615        d->pending.append(args);
    469616        return;
    470617    }
    471618
    472     doFlush(br, wbr.topLeft());
    473 }
    474 
    475 bool QPMDiveWindowSurface::adjustSetup()
     619    doFlush(widget, br, wbr.topLeft());
     620}
     621
     622bool QPMDiveWindowSurface::adjustSetup(QWidget *widget)
    476623{
    477624    HWND hwnd = window()->winId();
     
    481628    bool setupDirty = false;
    482629
    483     if (d->posDirty || d->vrnDirty) {
     630    QPMDiveWindowSurfacePrivate::WidgetData *wd = d->widgetData(widget);
     631    Q_ASSERT(wd);
     632
     633    if (d->posDirty || wd->vrnDirty) {
    484634        setupDirty = true;
    485635        d->posDirty = false;
     636        // the main widget moved, adjust the target poition
    486637        POINTL ptl = { 0, 0 };
    487638        WinMapWindowPoints(hwnd, HWND_DESKTOP, &ptl, 1);
     
    494645    }
    495646
    496     if (d->vrnDirty) {
     647    bool wasVrnDirty = wd->vrnDirty;
     648
     649    if (wd->vrnDirty) {
    497650        setupDirty = true;
    498         d->vrnDirty = false;
     651        wd->vrnDirty = false;
    499652
    500653        HPS hps = window()->getPS();
    501654        HRGN hrgn = GpiCreateRegion(hps, 0L, NULL);
    502655
    503         RGNRECT rgnCtl;
    504         rgnCtl.ircStart = 1;
    505         rgnCtl.crc = rgnCtl.crcReturned = 0;
    506         rgnCtl.ulDirection = RECTDIR_LFRT_TOPBOT;
    507 
    508656        ULONG rc = WinQueryVisibleRegion(hwnd, hrgn);
     657        if (rc != RGN_ERROR) {
     658            HWND hwndWidget = widget->winId();
     659            POINTL ptlOffset = { 0, 0 };
     660            if (hwnd != hwndWidget) {
     661                // translate the main widget's visible region to this widget's
     662                // coordinate space
     663                WinMapWindowPoints(hwnd, hwndWidget, &ptlOffset, 1);
     664                GpiOffsetRegion(hps, hrgn, &ptlOffset);
     665            }
     666            // substract children from the visible region, if any
     667            rc = qt_WinProcessWindowObstacles(hwndWidget, NULL, hrgn,
     668                                              CRGN_DIFF, PWO_Children);
     669            if (rc != RGN_ERROR && hwnd != hwndWidget) {
     670                // translate back to the main widget's coordinate space
     671                ptlOffset.x = -ptlOffset.x;
     672                ptlOffset.y = -ptlOffset.y;
     673                GpiOffsetRegion(hps, hrgn, &ptlOffset);
     674            }
     675        }
     676
    509677        if (rc == RGN_RECT || rc == RGN_COMPLEX) {
     678            RGNRECT rgnCtl;
     679            rgnCtl.ircStart = 1;
     680            rgnCtl.crc = rgnCtl.crcReturned = 0;
     681            rgnCtl.ulDirection = RECTDIR_LFRT_TOPBOT;
    510682            if (GpiQueryRegionRects(hps, hrgn, NULL, &rgnCtl, NULL) &&
    511683                rgnCtl.crcReturned) {
     
    513685                rgnCtl.crc = rgnCtl.crcReturned;
    514686                rgnCtl.ulDirection = RECTDIR_LFRT_TOPBOT;
    515                 if (d->rcls.size() < (int)rgnCtl.crc)
    516                     d->rcls.resize((int)rgnCtl.crc);
    517                 GpiQueryRegionRects(hps, hrgn, NULL, &rgnCtl, d->rcls.data());
     687                if (wd->rcls.size() < (int)rgnCtl.crc)
     688                    wd->rcls.resize((int)rgnCtl.crc);
     689                wd->rclCount = rgnCtl.crc;
     690                GpiQueryRegionRects(hps, hrgn, NULL, &rgnCtl, wd->rcls.data());
    518691            }
    519         }
    520 
    521         d->setup.ulNumDstRects = rgnCtl.crcReturned;
    522         d->setup.pVisDstRects = rgnCtl.crcReturned ? d->rcls.data() : NULL;
    523         d->setup.ulStructLen = sizeof(SETUP_BLITTER);
     692        } else if (rc == RGN_NULL) {
     693            wd->rclCount = 0;
     694        }
    524695
    525696        GpiDestroyRegion(hps, hrgn);
     
    528699        // memorize the window height used for the additional visible region
    529700        // validation in doFlush()
    530         d->windowHeight = window()->height();
     701        wd->widgetHeight = widget->height();
    531702
    532703#if defined(QDIVE_DEBUG)
    533704        DEBUG(() << "QPMDiveWindowSurface::adjustSetup:" << "vrnDirty");
    534         for (size_t i = 0; i < d->setup.ulNumDstRects; ++i)
    535             DEBUG(() << " " << i << ":" << d->setup.pVisDstRects[i]);
     705        for (size_t i = 0; i < wd->rclCount; ++i)
     706            DEBUG(() << " " << i << ":" << wd->rcls[i]);
    536707#endif
    537708    }
    538709
     710    // make sure setup points to the correct visible rectangle array (note that
     711    // we switch it even vrnDirty is false since the widget may change)
     712    if (wasVrnDirty || d->setup.ulNumDstRects != wd->rclCount ||
     713        (wd->rclCount && d->setup.pVisDstRects != wd->rcls.data()) ||
     714        (!wd->rclCount && d->setup.pVisDstRects != NULL)) {
     715        setupDirty = true;
     716        d->setup.ulNumDstRects = wd->rclCount;
     717        d->setup.pVisDstRects = wd->rclCount ? wd->rcls.data() : NULL;
     718        d->setup.ulStructLen = sizeof(SETUP_BLITTER);
     719    }
     720
    539721    return setupDirty;
    540722}
    541723
    542 void QPMDiveWindowSurface::doFlush(const QRect &from, const QPoint &to)
    543 {
    544     DEBUG(() << "QPMDiveWindowSurface::doFlush:" << window()
     724void QPMDiveWindowSurface::doFlush(QWidget *widget, const QRect &from, const QPoint &to)
     725{
     726    DEBUG(() << "QPMDiveWindowSurface::doFlush:" << window() << widget
    545727             << "from" << from << "to" << to);
    546728
     
    550732    QPoint dst = to + (src.topLeft() - from.topLeft());
    551733
    552     bool setupDirty = adjustSetup();
     734    bool setupDirty = adjustSetup(widget);
    553735
    554736    // note that the source image is expected to be top-left oriented
     
    578760
    579761    DiveBlitImage(d->hDive, d->bufNum, DIVE_BUFFER_SCREEN);
    580 }
    581 
    582 bool QPMDiveWindowSurface::pmEventFilter(QMSG *msg, MRESULT *result)
    583 {
    584     switch (msg->msg) {
    585         case WM_VRNDISABLED: {
    586             if (!d->useFB)
    587                 DiveSetupBlitter(d->hDive, NULL);
    588             d->vrnDisabled = true;
    589             *result = 0;
    590             return true;
    591         }
    592         case DM_DRAGOVER: {
    593             d->inDrag = true;
    594             break;
    595         }
    596         case DM_DRAGLEAVE:
    597         case DM_DROP: {
    598             d->inDrag = false;
    599             break;
    600         }
    601         case WM_VRNENABLED: {
    602             d->vrnDisabled = false;
    603             // Note that when an overlapping window of *other* process is moved
    604             // over this window, PM still sends WM_VRNENABLED to it but with
    605             // ffVisRgnChanged set to FALSE although the visible region *does*
    606             // actually change (it doesn't seem to be the case for windows of
    607             // the same process). The solution is to ignore this flag and always
    608             // assume that the visible region was changed when we get this msg
    609 #if 0
    610             if (LONGFROMMP(msg->mp1)) // window's visible region changed
    611 #endif
    612                 d->vrnDirty = true;
    613             d->posDirty = true;
    614 
    615             // process pending flush events
    616             foreach(const QPMDiveWindowSurfacePrivate::FlushArgs &args, d->pending)
    617                 doFlush(args.from, args.to);
    618             d->pending.clear();
    619 
    620             *result = 0;
    621             return true;
    622         }
    623         default:
    624             break;
    625     }
    626 
    627     return false;
    628762}
    629763
     
    9341068}
    9351069
    936 void QPMDiveWindowSurfaceFB::doFlush(const QRect &from, const QPoint &to)
    937 {
    938     DEBUG(() << "QPMDiveWindowSurfaceFB::doFlush:" << window()
     1070void QPMDiveWindowSurfaceFB::doFlush(QWidget *widget, const QRect &from, const QPoint &to)
     1071{
     1072    DEBUG(() << "QPMDiveWindowSurfaceFB::doFlush:" << window() << widget
    9391073             << "from" << from << "to" << to);
    9401074
     
    9461080    QPoint dstDelta = from.topLeft() - to;
    9471081
    948     const int windowHeight = window()->height();
     1082    QPMDiveWindowSurfacePrivate::WidgetData *wd = d->widgetData(widget);
     1083    Q_ASSERT(wd);
     1084
     1085    int widgetHeight = widget->height();
    9491086
    9501087    // sometimes PM does not send WM_VRNENABLED although the window size has
     
    9521089    // application) which leads to screen corruption due to outdated visible
    9531090    // regions. A fix is to memorize the window height and check it here
    954     if (windowHeight != d->windowHeight)
    955         d->vrnDirty = true;
     1091    if (widgetHeight != wd->widgetHeight)
     1092        wd->vrnDirty = true;
    9561093
    9571094    bool wasPosDirty = d->posDirty;
    958     bool wasVrnDirty = d->vrnDirty;
    959 
    960     adjustSetup();
     1095    bool wasVrnDirty = wd->vrnDirty;
     1096
     1097    adjustSetup(widget);
     1098
     1099    int windowHeight = window()->height();
    9611100
    9621101    if (wasVrnDirty) {
  • trunk/src/gui/painting/qwindowsurface_pm_p.h

    r713 r751  
    6363struct QPMDiveWindowSurfacePrivate;
    6464
    65 class QPMDiveWindowSurface : public QWindowSurface, private QWidget::PmEventFilter
     65class QPMDiveWindowSurface : public QWindowSurface
    6666{
    6767public:
     
    7777
    7878protected:
    79     bool adjustSetup();
    80     virtual void doFlush(const QRect &from, const QPoint &to);
    81     bool pmEventFilter(QMSG *msg, MRESULT *result);
     79    bool adjustSetup(QWidget *widget);
     80    virtual void doFlush(QWidget *widget, const QRect &from, const QPoint &to);
    8281
    8382    QScopedPointer<QPMDiveWindowSurfacePrivate> d;
     83    friend struct QPMDiveWindowSurfacePrivate;
    8484};
    8585
Note: See TracChangeset for help on using the changeset viewer.