diff options
-rw-r--r-- | src/mcompositemanager.cpp | 15 | ||||
-rw-r--r-- | src/mcompositescene.cpp | 44 | ||||
-rw-r--r-- | src/mtexturepixmapitem_p.cpp | 7 | ||||
-rw-r--r-- | src/mwindowpropertycache.cpp | 10 | ||||
-rw-r--r-- | src/mwindowpropertycache.h | 2 | ||||
-rwxr-xr-x | tests/functional/test16.py | 54 | ||||
-rw-r--r-- | tests/functional/test16.py.testdata | 9 | ||||
-rw-r--r-- | tests/windowctl/windowctl.cpp | 21 |
8 files changed, 133 insertions, 29 deletions
diff --git a/src/mcompositemanager.cpp b/src/mcompositemanager.cpp index 4901b4b..668407d 100644 --- a/src/mcompositemanager.cpp +++ b/src/mcompositemanager.cpp @@ -1818,7 +1818,12 @@ void MCompositeManagerPrivate::mapEvent(XMapEvent *e) else item->saveBackingStore(true); item->setVisible(true); - item->fadeIn(); + // TODO: don't show the animation if the window is not stacked on top + const XWMHints &h = pc->getWMHints(); + if (!(h.flags & StateHint) || h.initial_state != IconicState) + item->fadeIn(); + else + item->setNewlyMapped(false); goto stack_and_return; } @@ -1836,10 +1841,14 @@ void MCompositeManagerPrivate::mapEvent(XMapEvent *e) qDebug() << "Composition overhead (new pixmap):" << overhead_measure.elapsed(); #endif - if (item->isAppWindow()) + const XWMHints &h = pc->getWMHints(); + if ((!(h.flags & StateHint) || h.initial_state != IconicState) + && item->isAppWindow()) item->fadeIn(); - else + else { item->setVisible(true); + item->setNewlyMapped(false); + } // the current decorated window got mapped if (e->window == MDecoratorFrame::instance()->managedWindow() && diff --git a/src/mcompositescene.cpp b/src/mcompositescene.cpp index 36cb017..a2a437c 100644 --- a/src/mcompositescene.cpp +++ b/src/mcompositescene.cpp @@ -98,21 +98,41 @@ void MCompositeScene::setupOverlay(Window window, const QRect &geom, void MCompositeScene::drawItems(QPainter *painter, int numItems, QGraphicsItem *items[], const QStyleOptionGraphicsItem options[], QWidget *widget) { - for (int i = 0; i < numItems; ++i) { - MCompositeWindow *window = (MCompositeWindow *) items[i]; + QRegion visible(sceneRect().toRect()); + QList<QGraphicsItem*> to_paint; + QList<QStyleOptionGraphicsItem> paint_opts; + // visibility is determined from top to bottom + for (int i = numItems - 1; i >= 0; --i) { + MCompositeWindow *cw = (MCompositeWindow *) items[i]; - // Redraw only textures which don't have opaque textures above it - if (((i < numItems - 1) - && (items[i+1]->sceneMatrix().mapRect(items[i]->boundingRect()) == - items[i]->boundingRect()) - && (!((MCompositeWindow *)items[i+1])->propertyCache()->hasAlpha()) - && (((MCompositeWindow *)items[i+1])->opacity() == 1.0)) - || window->isIconified()) - continue; + if (visible.isEmpty()) + // nothing below is visible anymore + break; + + // FIXME: this region is always the same as the window's shape, + // some transformations would be needed... + QRegion r(cw->sceneMatrix().map(cw->propertyCache()->shapeRegion())); + // transitioning window can be smaller than shapeRegion(), so paint + // all transitioning windows + if (cw->isWindowTransitioning() || visible.intersects(r)) { + to_paint.prepend(cw); + paint_opts.prepend(options[i]); + } + + // subtract opaque regions + if (!cw->isWindowTransitioning() + && !cw->propertyCache()->hasAlpha() && cw->opacity() == 1.0) + visible -= r; + } + // paint from bottom to top so that blending works + while (!to_paint.isEmpty()) { + // TODO: paint only the intersected region (glScissor?) + MCompositeWindow *cw = (MCompositeWindow*)to_paint.takeFirst(); painter->save(); - painter->setMatrix(items[i]->sceneMatrix(), true); - items[i]->paint(painter, &options[i], widget); + painter->setMatrix(cw->sceneMatrix(), true); + QStyleOptionGraphicsItem opts = paint_opts.takeFirst(); + cw->paint(painter, &opts, widget); painter->restore(); } } diff --git a/src/mtexturepixmapitem_p.cpp b/src/mtexturepixmapitem_p.cpp index 223b442..4343a82 100644 --- a/src/mtexturepixmapitem_p.cpp +++ b/src/mtexturepixmapitem_p.cpp @@ -255,12 +255,7 @@ void MTexturePixmapPrivate::damageTracking(bool enabled) void MTexturePixmapPrivate::saveBackingStore(bool renew) { - XWindowAttributes a; - if (!XGetWindowAttributes(QX11Info::display(), item->window(), &a)) { - qWarning("%s: invalid window 0x%lx", __func__, item->window()); - return; - } - if (a.map_state != IsViewable) + if (item->propertyCache()->is_valid && !item->propertyCache()->isMapped()) return; if (windowp) diff --git a/src/mwindowpropertycache.cpp b/src/mwindowpropertycache.cpp index a11f82c..764d896 100644 --- a/src/mwindowpropertycache.cpp +++ b/src/mwindowpropertycache.cpp @@ -218,19 +218,19 @@ bool MWindowPropertyCache::hasAlpha() return has_alpha ? true : false; } -const QRegion MWindowPropertyCache::shapeRegion() +const QRegion &MWindowPropertyCache::shapeRegion() { if (shape_rects_valid) { if (shape_region.isEmpty()) - return QRegion(realGeometry()); - else - return shape_region; + shape_region = QRegion(realGeometry()); + return shape_region; } xcb_shape_get_rectangles_reply_t *r; r = xcb_shape_get_rectangles_reply(xcb_conn, xcb_shape_rects_cookie, 0); if (!r) { shape_rects_valid = true; - return QRegion(realGeometry()); + shape_region = QRegion(realGeometry()); + return shape_region; } xcb_rectangle_iterator_t i; i = xcb_shape_get_rectangles_rectangles_iterator(r); diff --git a/src/mwindowpropertycache.h b/src/mwindowpropertycache.h index 5f049a0..e75a5d0 100644 --- a/src/mwindowpropertycache.h +++ b/src/mwindowpropertycache.h @@ -80,7 +80,7 @@ public: } return real_geom; } - const QRegion shapeRegion(); + const QRegion &shapeRegion(); void shapeRefresh() { if (!shape_rects_valid) shapeRegion(); diff --git a/tests/functional/test16.py b/tests/functional/test16.py new file mode 100755 index 0000000..80cefdd --- /dev/null +++ b/tests/functional/test16.py @@ -0,0 +1,54 @@ +#!/usr/bin/python + +# Check that initial_state=IconicState is respected in WM_HINTS + +#* Test steps +# * show an application window with initial_state=IconicState +#* Post-conditions +# * application is stacked under the desktop window + +import os, re, sys, time + +if os.system('/sbin/mcetool --unblank-screen --set-inhibit-mode=stay-on'): + print 'mcetool is missing!' + +if os.system('pidof mcompositor'): + print 'mcompositor is not running' + sys.exit(1) + +fd = os.popen('windowstack m') +s = fd.read(5000) +win_re = re.compile('^0x[0-9a-f]+') +home_win = 0 +for l in s.splitlines(): + if re.search(' DESKTOP viewable ', l.strip()): + home_win = win_re.match(l.strip()).group() + break + +if home_win == 0: + print 'FAIL: desktop not found' + sys.exit(1) + +# create initially iconic application window +fd = os.popen('windowctl In') +old_win = fd.readline().strip() +time.sleep(2) + +ret = 0 +fd = os.popen('windowstack m') +s = fd.read(5000) +for l in s.splitlines(): + if re.search("%s " % home_win, l.strip()): + print home_win, 'found' + break + elif re.search("%s " % old_win, l.strip()): + print 'FAIL: application window is not iconic' + print 'Failed stack:\n', s + ret = 1 + break + +# cleanup +os.popen('pkill windowctl') +time.sleep(1) + +sys.exit(ret) diff --git a/tests/functional/test16.py.testdata b/tests/functional/test16.py.testdata new file mode 100644 index 0000000..827057f --- /dev/null +++ b/tests/functional/test16.py.testdata @@ -0,0 +1,9 @@ +CaseName="initial_state_iconic_is_respected" +CaseRequirement="NONE" +CaseTimeout="360" +CaseDescription="Check that initial_state=IconicState is respected in WM_HINTS.\n +\n +- Test steps\n +\t- show an application window with initial_state=IconicState\n +- Post-conditions\n +\t- application is stacked under the desktop window\n" diff --git a/tests/windowctl/windowctl.cpp b/tests/windowctl/windowctl.cpp index b4d3ad6..9580d41 100644 --- a/tests/windowctl/windowctl.cpp +++ b/tests/windowctl/windowctl.cpp @@ -308,7 +308,7 @@ static void wait_for_mapnotify(Display *dpy, Window w) static void print_usage_and_exit(QString& stdOut) { #define PROG "windowctl" - stdOut = "Usage 1: " PROG " [afoemks](n|d|i|b) [transient for <XID>]\n" + stdOut = "Usage 1: " PROG " [afoemksI](n|d|i|b) [transient for <XID>]\n" "a - ARGB (32-bit) window, otherwise 16-bit is used\n" "f - fullscreen window\n" "o - override-redirect window\n" @@ -316,6 +316,7 @@ static void print_usage_and_exit(QString& stdOut) "m - set _NET_WM_STATE_MODAL (makes sense for dialogs only)\n" "k - set _KDE_NET_WM_WINDOW_TYPE_OVERRIDE\n" "s - make the window shaped\n" + "I - use initial_state = IconicState in WM_HINTS\n" "h - set _MEEGOTOUCH_DECORATOR_BUTTONS for home and close buttons\n" "n - WM_TYPE_NORMAL window (if 'k' is given, that is the first type)\n" "d - WM_TYPE_DIALOG window\n" @@ -378,6 +379,17 @@ static void set_group (Display *dpy, char *first, char *second) XSync(dpy, False); } +static void set_initial_state_iconic (Display *dpy, Window w) +{ + XWMHints h; + memset(&h, 0, sizeof(h)); + + h.flags = StateHint; + h.initial_state = IconicState; + XSetWMHints(dpy, w, &h); + XSync(dpy, False); +} + static void do_command (Display *dpy, char command, Window window, Window target, QString& stdOut) { @@ -546,7 +558,7 @@ static bool old_main(QStringList& args, QString& stdOut) time_t last_time; int argb = 0, fullscreen = 0, override_redirect = 0, decor_buttons = 0, exit_on_unmap = 1, modal = 0, kde_override = 0, meego_layer = -1, - shaped = 0; + shaped = 0, initial_iconic = 0; WindowType windowtype = TYPE_INVALID; if (args.count() < 1 || args.count() > 4) { @@ -594,6 +606,10 @@ static bool old_main(QStringList& args, QString& stdOut) shaped = 1; continue; } + if (*p == 'I') { + initial_iconic = 1; + continue; + } if (*p == 'h') { decor_buttons = 1; continue; @@ -724,6 +740,7 @@ static bool old_main(QStringList& args, QString& stdOut) if (decor_buttons) set_decorator_buttons(dpy, w); if (shaped) set_shaped(dpy, w); + if (initial_iconic) set_initial_state_iconic(dpy, w); green_gc = XCreateGC (dpy, w, 0, NULL); XParseColor (dpy, colormap, green, &green_col); |