summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Wu <peter@lekensteyn.nl>2016-10-11 02:10:16 (GMT)
committerPeter Wu <peter@lekensteyn.nl>2016-10-13 00:12:42 (GMT)
commitaab52c83ba70e9867632e83bd81715fc4405ffcf (patch)
tree578624f479af307cb768e11c5dc0de5aeae6db34
parente4c2e564a5b91497132d9a20d8f521af405286bd (diff)
Capture all windows for "Window under cursor"
Capture the correct area, not just the top-left one (bug 369330). Capture all possible related windows, not just the top-level (or the non-transient background window), this will correctly capture whole applications including the open menus (bug 357223). The last change also affects how windows with other overlapping ones are captured, previously the hidden area is shown as if there was no popup. For example, Kate would be captured and not its Open dialog. Tested with Kate (Qt 5): Open File -> Add Places Entry -> Select Icon. Verify that clicking all of these menus show the same screenshot and that the non-overlapping parts are shown. Right-click to open a context menu, this is also captured on the delayed screenshot. Tested with heimdall-frontend and Hedgewars (Qt 4): open a menu, keep the cursor on it. Verify that the menu and window are captured. Do the same, but with the cursor outside the menu. REVIEW: 129163 BUG: 357223 BUG: 369330 FIXED-IN: 16.12
-rw-r--r--src/PlatformBackends/X11ImageGrabber.cpp91
-rw-r--r--src/PlatformBackends/X11ImageGrabber.h3
2 files changed, 38 insertions, 56 deletions
diff --git a/src/PlatformBackends/X11ImageGrabber.cpp b/src/PlatformBackends/X11ImageGrabber.cpp
index 0525ec4..c1d9499 100644
--- a/src/PlatformBackends/X11ImageGrabber.cpp
+++ b/src/PlatformBackends/X11ImageGrabber.cpp
@@ -35,6 +35,7 @@
#include <QGraphicsScene>
#include <QGraphicsPixmapItem>
#include <QGraphicsDropShadowEffect>
+#include <QSet>
#include <KWindowSystem>
#include <KWindowInfo>
@@ -379,51 +380,44 @@ void X11ImageGrabber::grabTransientWithParent()
{
xcb_window_t curWin = getRealWindowUnderCursor();
- // do we have a top-level or a transient window?
-
- KWindowInfo info(curWin, NET::WMName, NET::WM2TransientFor | NET::WM2WindowClass);
- if (!(info.valid(true) && (info.transientFor() != XCB_WINDOW_NONE)) ||
- info.windowClassClass().isEmpty() || info.windowClassName().isEmpty()) {
- return grabWindowUnderCursor();
- }
-
// grab the image early
mPixmap = getWindowPixmap(QX11Info::appRootWindow(), false);
// now that we know we have a transient window, let's
- // see if the parent has any other transient windows who's
- // transient for the same app
+ // find other possible transient windows and the app window itself.
QRegion clipRegion;
- QStack<xcb_window_t> childrenStack = findAllChildren(findParent(curWin));
- while (!(childrenStack.isEmpty())) {
- xcb_window_t winId = childrenStack.pop();
- KWindowInfo tempInfo(winId, 0, NET::WM2TransientFor);
-
- if (info.transientFor() == tempInfo.transientFor()) {
- clipRegion += getApplicationWindowGeometry(winId);
- }
- }
+ QSet<xcb_window_t> transients;
+ xcb_window_t parentWinId = curWin;
+ do {
+ // find parent window and add the window to the visible region
+ xcb_window_t winId = parentWinId;
+ QRect winRect;
+ parentWinId = getTransientWindowParent(winId, winRect);
+ transients << winId;
+ clipRegion += winRect;
- // now we have a list of all the transient windows for the
- // parent, time to find the parent
+ // Continue walking only if this is a transient window (having a parent)
+ } while (parentWinId != XCB_WINDOW_NONE && !transients.contains(parentWinId));
- QList<WId> winList = KWindowSystem::stackingOrder();
- for (int i = winList.size() - 1; i >= 0; i--) {
- KWindowInfo tempInfo(winList[i], NET::WMGeometry | NET::WMFrameExtents, NET::WM2WindowClass);
- QString winClass(tempInfo.windowClassClass());
- QString winName(tempInfo.windowClassName());
+ // All parents are known now, find other transient children.
+ // Assume that the lowest window is behind everything else, then if a new
+ // transient window is discovered, its children can then also be found.
- if (winClass.contains(info.name(), Qt::CaseInsensitive) || winName.contains(info.name(), Qt::CaseInsensitive)) {
- if (mCaptureDecorations) {
- clipRegion += tempInfo.frameGeometry();
- } else {
- clipRegion += tempInfo.geometry();
+ QList<WId> winList = KWindowSystem::stackingOrder();
+ for (auto winId : winList) {
+ QRect winRect;
+ xcb_window_t parentWinId = getTransientWindowParent(winId, winRect);
+
+ // if the parent should be displayed, then show the child too
+ if (transients.contains(parentWinId)) {
+ if (!transients.contains(winId)) {
+ transients << winId;
+ clipRegion += winRect;
}
- break;
}
}
@@ -644,29 +638,18 @@ xcb_window_t X11ImageGrabber::getRealWindowUnderCursor()
return pointerReply->child;
}
-QStack<xcb_window_t> X11ImageGrabber::findAllChildren(xcb_window_t window)
-{
- QStack<xcb_window_t> winStack;
- xcb_connection_t *xcbConn = QX11Info::connection();
- xcb_query_tree_cookie_t treeCookie = xcb_query_tree_unchecked(xcbConn, window);
- CScopedPointer<xcb_query_tree_reply_t> treeReply(xcb_query_tree_reply(xcbConn, treeCookie, NULL));
- xcb_window_t *winChildren = xcb_query_tree_children(treeReply.data());
- int winChildrenLength = xcb_query_tree_children_length(treeReply.data());
-
- for (int i = winChildrenLength - 1; i >= 0; i--) {
- winStack.push(winChildren[i]);
- }
-
- return winStack;
-}
-
-xcb_window_t X11ImageGrabber::findParent(xcb_window_t window)
+// obtain the size of the given window, returning the window ID of the parent
+xcb_window_t X11ImageGrabber::getTransientWindowParent(xcb_window_t winId, QRect &outRect)
{
- xcb_connection_t *xcbConn = QX11Info::connection();
-
- xcb_query_tree_cookie_t treeCookie = xcb_query_tree_unchecked(xcbConn, window);
- CScopedPointer<xcb_query_tree_reply_t> treeReply(xcb_query_tree_reply(xcbConn, treeCookie, NULL));
+ NET::Properties properties = mCaptureDecorations ? NET::WMFrameExtents : NET::WMGeometry;
+ KWindowInfo winInfo(winId, properties, NET::WM2TransientFor);
- return treeReply->parent;
+ // add the current window to the image
+ if (mCaptureDecorations) {
+ outRect = winInfo.frameGeometry();
+ } else {
+ outRect = winInfo.geometry();
+ }
+ return winInfo.transientFor();
}
diff --git a/src/PlatformBackends/X11ImageGrabber.h b/src/PlatformBackends/X11ImageGrabber.h
index 513bf4b..ee32999 100644
--- a/src/PlatformBackends/X11ImageGrabber.h
+++ b/src/PlatformBackends/X11ImageGrabber.h
@@ -78,10 +78,9 @@ class X11ImageGrabber : public ImageGrabber
xcb_window_t getRealWindowUnderCursor();
void grabApplicationWindowHelper(xcb_window_t window);
QRect getApplicationWindowGeometry(xcb_window_t window);
- QStack<xcb_window_t> findAllChildren(xcb_window_t window);
- xcb_window_t findParent(xcb_window_t window);
QPixmap getWindowPixmap(xcb_window_t window, bool blendPointer);
QPixmap convertFromNative(xcb_image_t *xcbImage);
+ xcb_window_t getTransientWindowParent(xcb_window_t winId, QRect &outRect);
OnClickEventFilter *mNativeEventFilter;
};