Xlib 应用被覆盖遮挡或者移出屏幕时,获取窗口截图

Posted 柴承训

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Xlib 应用被覆盖遮挡或者移出屏幕时,获取窗口截图相关的知识,希望对你有一定的参考价值。

https://www.cnblogs.com/chaichengxun/p/15409996.html这篇文章里已经讲过,如果应用窗口位于屏幕外或者被遮挡,xlib不能获取完整的应用窗口截屏,如下

 

 

今天就拉解决这个问题。

用到的头文件:

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/Xcomposite.h>
#include <X11/extensions/Xrender.h>

用到的库:

-lXlib -lXcomposite -lXrender

代码:

static Display *xdisplay = 0;

Display *disp()
{
    if (!xdisplay)
        xdisplay = XOpenDisplay(NULL);

    return xdisplay;
}

XImage* GetDrawableThumbnail(Drawable src_drawable,
                             Visual* visual,
                             int src_width,
                             int src_height) {
    XRenderPictFormat* format = XRenderFindVisualFormat(disp(),
                                                        visual);
    if (!format) {
        cout <<"XRenderFindVisualFormat() failed";
        return NULL;
    }

    XRenderPictureAttributes pa;
    pa.subwindow_mode = IncludeInferiors;
    Picture src = XRenderCreatePicture(disp(),
                                       src_drawable,
                                       format,
                                       CPSubwindowMode,
                                       &pa);
    if (!src) {
        cout << "XRenderCreatePicture() failed";
        return NULL;
    }

    Pixmap dst_pixmap = XCreatePixmap(disp(),
                                      src_drawable,
                                      src_width,
                                      src_height,
                                      format->depth);
    if (!dst_pixmap) {
        cout << "XCreatePixmap() failed";
        XRenderFreePicture(disp(), src);
        return NULL;
    }
    Picture dst = XRenderCreatePicture(disp(), dst_pixmap, format, 0, NULL);
    if (!dst) {
        cout << "XRenderCreatePicture() failed";
        XFreePixmap(disp(), dst_pixmap);
        XRenderFreePicture(disp(), src);
        return NULL;
    }

    XRenderColor transparent = {0};
    XRenderFillRectangle(disp(),
                         PictOpSrc,
                         dst,
                         &transparent,
                         0,
                         0,
                         src_width,
                         src_height);

    XTransform xform = { {
                             { XDoubleToFixed(1), XDoubleToFixed(0), XDoubleToFixed(0) },
                             { XDoubleToFixed(0), XDoubleToFixed(1), XDoubleToFixed(0) },
                             { XDoubleToFixed(0), XDoubleToFixed(0), XDoubleToFixed(1) }
                         } };
    XRenderSetPictureTransform(disp(), src, &xform);
    XRenderSetPictureFilter(disp(), src, FilterBest, NULL, 0);
    XRenderComposite(disp(),
                     PictOpSrc,
                     src,
                     None,
                     dst,
                     0,
                     0,
                     0,
                     0,
                     0,
                     0,
                     src_width,
                     src_height);
    XImage* image = XGetImage(disp(),
                              dst_pixmap,
                              0,
                              0,
                              src_width,
                              src_height,
                              AllPlanes, ZPixmap);
    return image;
}

将XImage转换成QPixmap:

void AppListSelDlg::GetPixmap(unsigned long win, QPixmap &pixmap)
{
    Display *display_ = disp();

    XCompositeRedirectWindow(display_, win, CompositeRedirectAutomatic);
    Pixmap src_pixmap = XCompositeNameWindowPixmap(display_, win);
    if (!src_pixmap) {
        printf("XCompositeNameWindowPixmap() failed");
        return;
    }

    XWindowAttributes attr;
    if (!XGetWindowAttributes(display_, win, &attr)) {
        printf( "XGetWindowAttributes() failed");
        XFreePixmap(display_, src_pixmap);
        return ;
    }

    XImage* pImage = XCompcap::GetDrawableThumbnail(src_pixmap,
                                          attr.visual,
                                          attr.width,
                                          attr.height);

    QImage image = QImage((const uchar *)(pImage->data), pImage->width, pImage->height, pImage->bytes_per_line, QImage::Format_RGB32);
    pixmap = QPixmap::fromImage(image);
    XDestroyImage(pImage);
    XFreePixmap(display_, src_pixmap);
}

 

是否可以检测窗口遮挡

使用AWT,我想通过在隐藏窗口时停止绘制到屏幕来保存像flash一样的资源。但首先,我需要一种方法来检测Frame是否被一个或多个其他窗口完全覆盖。 Windows可能不是来自同一个应用程序,所以我不能只是总结他们的形状。

所以问题是,是否可以检测窗口是否被其他应用程序的其他窗口覆盖?

答案

一切皆有可能只需要一些创造力和努力工作:)

我希望你知道Java可以调用本机Windows API(这不是非常高性能的vise,但我们只有这种方式),为此我们可以使用JNA lib,这将使我们能够访问本机共享库。

我已经做了一些快速的概念检查,这个代码只检查活动窗口是否完全覆盖了java应用程序窗口,但是你可以迭代可见窗口并计算区域,JNA也提供了访问权限。

对于我的演示项目,我使用了这些JNA依赖项:

compile("net.java.dev.jna", "jna", "4.5.0")
compile("net.java.dev.jna", "jna-platform", "4.5.0")

我的主要课程:

package com.sauliuxx.inc;

import com.sauliuxx.inc.workers.ActiveWindowChecker;
import com.sun.jna.platform.win32.WinDef;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * The type App.
 */
public class App extends Frame implements ActionListener {

    private final String title = "Demo123";
    private Label sizeLabel;
    private Label positionLabel;
    private Label visibleLabel;

    BlockingQueue<WinDef.RECT> q =
            new LinkedBlockingQueue<>();

    private App() {
        this.setTitle(title);
        this.setLayout(new BorderLayout());
        this.setSize(500, 500);
        Panel infoPanel = new Panel();
        sizeLabel = new Label(this.getSize().height + " X " + this.getSize().width);
        infoPanel.add(sizeLabel);
        positionLabel = new Label("X: " + this.getLocation().getX() + " Y: " + this.getLocation().getY());
        infoPanel.add(positionLabel);
        visibleLabel = new Label(this.isVisible() ? "true" : "false");
        infoPanel.add(visibleLabel);
        this.add(infoPanel, BorderLayout.PAGE_END);

        Timer timer = new Timer(250, this);
        timer.start();

        this.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });

        this.addComponentListener(new ComponentListener() {
            @Override
            public void componentResized(ComponentEvent componentEvent) {
                sizeLabel.setText(componentEvent.getComponent().getSize().height + " X " + componentEvent.getComponent().getSize().width);
            }

            @Override
            public void componentMoved(ComponentEvent componentEvent) {
                positionLabel.setText("X: " + componentEvent.getComponent().getLocation().getX() + " Y: " + componentEvent.getComponent().getLocation().getY());
            }

            @Override
            public void componentShown(ComponentEvent componentEvent) {
                visibleLabel.setText("true");
            }

            @Override
            public void componentHidden(ComponentEvent componentEvent) {
                visibleLabel.setText("false");
            }
        });

        ActiveWindowChecker awcDeamon = new ActiveWindowChecker(q);
        awcDeamon.setDaemon(true);
        awcDeamon.start();

    }

    @Override
    public void actionPerformed(ActionEvent actionEvent) {

        WinDef.RECT rect = null;

        try {
            rect = q.take();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if (this.isActive()) {
            System.out.println("action");
        } else {
            //System.out.println("rect = " + (rect != null ? rect : ""));
            //System.out.println("frame = [(" + (int)this.getLocation().getX() + "," + (int)this.getLocation().getY() + ") (" + this.getSize().width + "," + this.getSize().height + ")]");

            // x and y windows to compare top left point
            int rxTop = rect == null ? 0: rect.left;
            int ryTop = rect == null ? 0:rect.top;
            int fxTop = (int) this.getLocation().getX();
            int fyTop = (int) this.getLocation().getY();

            // bottom right points
            int rxBottom = rect == null ? 0: rect.right;
            int ryBottom = rect == null ? 0: rect.bottom;
            int fxBottom = fxTop + this.getSize().width;
            int fyBottom = fyTop + this.getSize().height;

            if ((rxTop >= fxTop || ryTop >= fyTop) || (rxBottom <= fxBottom || ryBottom <= fyBottom))
            {
                System.out.println("Not covered by active window.");
            }
            else{
                System.out.println("Covered by active window.");
            }
        }
    }

    /**
     * The entry point of application.
     *
     * @param args the input arguments
     */
    public static void main(String... args) {

        if (!System.getProperty("os.name").contains("Windows")) {
            System.err.println("ERROR: Only implemented on Windows");
            System.exit(1);
        }

        java.awt.EventQueue.invokeLater(() -> new App().setVisible(true));
    }
}

我的工人班:

package com.sauliuxx.inc.workers;

import com.sun.jna.Native;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef;

import java.util.concurrent.BlockingQueue;

/**
 * The type Active window checker.
 */
public class ActiveWindowChecker extends Thread {

    private static final int MAX_TITLE_LENGTH = 1024;

    private final BlockingQueue<WinDef.RECT> queue;

    /**
     * Instantiates a new Active window checker.
     *
     * @param q the q
     */
    public ActiveWindowChecker(BlockingQueue<WinDef.RECT> q) {
        this.queue = q;
    }

    @Override
    public void run() {
        Exception ex = null;
        while (ex == null) {
            char[] buffer = new char[MAX_TITLE_LENGTH * 2];
            WinDef.HWND hwnd = User32.INSTANCE.GetForegroundWindow();
            User32.INSTANCE.GetWindowText(hwnd, buffer, MAX_TITLE_LENGTH);
            System.out.println("Active window title: " + Native.toString(buffer));
            WinDef.RECT rect = new WinDef.RECT();
            User32.INSTANCE.GetWindowRect(hwnd, rect);
            try {
                queue.put(rect);
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
                ex = e;
            }
        }
    }
}

以上是关于Xlib 应用被覆盖遮挡或者移出屏幕时,获取窗口截图的主要内容,如果未能解决你的问题,请参考以下文章

delphi+被完全遮挡的窗口如何截图保存?

C#制作截图软件时,怎样不截取软件窗口本身? 看下图:软件自身也被截下来了

高德地图marker的label被别的marker遮挡

有没有办法在点击时从地图框弹出窗口中获取信息?

将 DirectX 精灵移出屏幕时如何防止其被擦除

移动端键盘遮挡input问题