首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >X11: XGetWindowAttributes:窗口位置偏移

X11: XGetWindowAttributes:窗口位置偏移
EN

Stack Overflow用户
提问于 2022-07-07 14:48:10
回答 1查看 79关注 0票数 1

我在Ubuntu 20.04上使用Gnome。当我使用XGetWindowAttributes()时,我得到的窗口位置有一个偏移,好像窗口有一个很厚的(不可见的)边距,但是对于不同的窗口,偏移量是不同的。

我查看了窗口属性中的组件border_width,但始终为零。我试过XTranslateWindowCoordinates() (https://stackoverflow.com/a/23940869/3852630),但它不影响职位。

为了给出一些细节,也许它们是相关的:我看到的窗口来自XGetInputFocus(),我使用这个答案https://stackoverflow.com/a/39549363/3852630中的代码来获取聚焦窗口的父窗口。Gnome在窗口周围画一个阴影,但是对于所有的窗口都应该是一样的。

知道我在哪里可以得到关于这个边距的大小或真正的窗口位置的信息吗?

非常感谢!

作为对大灰狼的评论的回应,这里是树。我使用XGetInputFocus获取焦点窗口,然后使用XQueryTree沿着窗口树向下移动,直到到达一个以根窗口为父窗口的窗口(请参阅上面的链接),树中的最后一个窗口将在下面列出。我打印窗口位置、窗口大小和窗口名称(如果有);n是树中下降的循环索引。

这是emacs窗口的树(有一个小的边框,即有一个小的位置偏移):

代码语言:javascript
运行
复制
n=1 x=-1 y=-1 w=1 h=1
n=2 x=10 y=45 w=752 h=714 name = emacs@laptopC3
n=3 x=3064 y=104 w=772 h=769

n=3的窗口位置适合我的双监视器设置,但行n=2提供一个较小的大小(但完全错误的位置)。我想知道n=1线上的窗口是什么。

这是一个终端的树(有一个大的边框):

代码语言:javascript
运行
复制
n=1 x=-1 y=-1 w=1 h=1
n=2 x=2889 y=390 w=786 h=533 name = Terminal

没什么可看的,除了n=1奇怪的窗口。

这是一个Tcl/Tk应用程序(它的边框很小):

代码语言:javascript
运行
复制
n=1 x=1 y=38 w=1026 h=770 name = PNGShow-6-6
n=2 x=3192 y=234 w=1028 h=809

请注意,这里没有n=1的奇怪窗口。

我看不出有一致的效果。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-07-10 16:52:18

在我的~/misc目录这堆东西中翻找了一番之后,我实际上找到了我在注释中提到的X11窗口树遍历程序的WIP。我最初写这篇文章是为了可视化窗口管理器创建装饰和将客户端放置在其中的不同方式。本质上,它是一个屏幕截图工具(根窗口除外),它为每个窗口编写了一个带有<image>对象的SVGs,并在其周围加上一个红色边框(类似于您如何使用border; solid red 1px;调试CSS ),显示了底层X11 drawable的内容。

这是源代码。您需要stb_image (用于PNG压缩)、cppcodec (用于base64编码)和xcb头来构建:

代码语言:javascript
运行
复制
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>

#include <sstream>
#include <iostream>
#include <type_traits>

#include <xcb/xcb.h>
#include <xcb/xcb_image.h>
#include <cppcodec/base64_url.hpp>
#include <cppcodec/base64_rfc4648.hpp>

#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"

static
void stringstream_write_b64(void *buf, void *data, int size)
{
    auto v = reinterpret_cast<std::vector<uint8_t>*>(buf);
    v->insert(v->end(), (uint8_t*)data, (uint8_t*)data+size);
}

std::string x11drawable_to_b64png(
    xcb_connection_t *const conn,
    xcb_drawable_t drawable,
    xcb_translate_coordinates_cookie_t const *const tcc )
{
    std::ostringstream s;

    xcb_generic_error_t *err_gg = nullptr, *err_tc = nullptr;
    auto const r_gg = xcb_get_geometry_reply(conn, xcb_get_geometry(conn, drawable), &err_gg);
    auto const r_tc = tcc ? xcb_translate_coordinates_reply(conn, *tcc, &err_tc) : nullptr;
    if( r_gg ){
        int ox = 0, oy = 0;
        if( r_tc ){
            ox = r_tc->dst_x; oy = r_tc->dst_y;
        } else {
            ox += r_gg->x; oy += r_gg->y;
        }

        xcb_image_t * const image =
            xcb_image_get(conn, drawable,
                0, 0, r_gg->width, r_gg->height,
                0xFFFFFFFF, XCB_IMAGE_FORMAT_Z_PIXMAP );
        if( image ){
            s << "<g>";
            s << "<image "
                << "x=\"" << ox << "\" "
                << "y=\"" << oy << "\" "
                << "width=\"" << r_gg->width << "\" "
                << "height=\"" << r_gg->height << "\" "
                << "xlink:href=\"data:image/png;base64,";
            std::ostringstream fn;
            fn << drawable << ".png";
            auto str = fn.str();

            for(size_t y = 0; y < image->height; ++y){
                auto r = (uint32_t*)(
                    (uint8_t*)image->data + y*image->stride );
                for(size_t x = 0; x < image->width; ++x ){
                    r[x] |= 0xFF000000;
                }
            }

            std::vector<uint8_t> buf;
            stbi_write_png_to_func(
                stringstream_write_b64, reinterpret_cast<void*>(&buf),
                image->width, image->height, 4,
                image->data, image->stride );
            s << cppcodec::base64_rfc4648::encode(buf);
            s << "\"/>\n";

            s << "<rect "
                << "x=\"" << ox << "\" "
                << "y=\"" << oy << "\" "
                << "width=\"" << r_gg->width << "\" "
                << "height=\"" << r_gg->height << "\" "
                << "style=\"fill:none;fill-opacity:1;stroke:#f00;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;\"/>";
            s << "</g>\n";
        }
    }

    free(err_gg); free(err_tc); free(r_gg); free(r_tc);

    return s.str();
}

static void traverse_window_tree(
    xcb_connection_t *conn,
    xcb_window_t const win,
    int const level )
{

    xcb_generic_error_t *err_qt = nullptr;
    auto const r_qt = xcb_query_tree_reply(conn, xcb_query_tree(conn, win), &err_qt);
    if( r_qt ){
        auto const root = r_qt->root;
        auto const children = xcb_query_tree_children(r_qt);
        auto const n = xcb_query_tree_children_length(r_qt);
        for( std::remove_const<decltype(n)>::type i = 0; i < n; ++i ){
            auto const child = children[i];

            for(unsigned l=0; l<level;++l){ std::cerr << " "; }
            std::cerr << child << std::endl;

            int c_ox = 0, c_oy = 0;
            auto const xtcc = xcb_translate_coordinates(conn, child, root, 0, 0);
            std::cout << x11drawable_to_b64png(conn, child, &xtcc);

            traverse_window_tree(conn, child, level+1);
        }
    }

    free(r_qt);
    free(err_qt);
}

static xcb_generic_event_t *xcb_poll_for_event_timeout(
    xcb_connection_t *const conn,
    unsigned timeout_us )
{
    unsigned i = 0;
    while( timeout_us >> (2+i) ){ ++i; };
    i /= 2;

    xcb_generic_event_t *ev;
    while( !(ev = xcb_poll_for_event(conn)) && i ){
        unsigned const t = timeout_us >> (1 + (i--));
        usleep( t );
    }
    return ev;
}

int main(int argc, char *argv[])
{
    int rc = 0;

    int screen;
    auto const conn = xcb_connect(NULL, &screen);
    if( !conn ){ rc = -1; }

    auto const xsetup = xcb_get_setup(conn);
    if( !xsetup ){ rc = -2; }

    auto xroots_iter = xcb_setup_roots_iterator(xsetup);
    for(int i = 0; i < screen; ++i){ xcb_screen_next(&xroots_iter); }
    auto const xscreen = reinterpret_cast<xcb_screen_t*>(xroots_iter.data);
    if( !xscreen ){ rc = -4; }

    std::cout << "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">" << std::endl;
    traverse_window_tree(conn, xscreen->root, 0);
    std::cout << "</svg>" << std::endl;

    if( conn ){ xcb_disconnect(conn); }
    return rc;
}

转储Gnome Shell会话会产生以下结果:左边的列是gtk-demo和gtk 3-demo,显示了对话框示例。中心顶部是gtk4 4-演示。在右上角是侏儒控制中心,吹掉它。在中间和右边是一个非常老的版本的CadSoft鹰运行,它使用IIRC Qt2或Qt3 (不知道具体是哪个)。在中心下面是KDE Dolphin的一个实例,在右下角运行一个DDD实例,它使用Motif。(在https://dl.datenwolf.net/xsvgtree/gnome-shell.svg上找到原始的SVG -在本地保存它,因为我的服务器的Content-Security-Policy不允许浏览器直接显示内联数据)。

您可以立即看到的是,各种工具箱在构造它们的窗口和将它们映射到屏幕上有多么不同。旧的工具箱将在他们的客户区域内创建许多小的、微小的子窗口,通常是为每个用户提供一个可交互的控件。另一方面,现代GTK和Qt只选择创建一个要绘制的大面,并使用它们自己的方法,然后将其传输到X11服务器。GTK的最新迭代实际上选择了完全绕过窗口管理器的装饰工具,只绘制客户机中的所有内容。而在侏儒控制中心,发生任何事情的窗口都比可见区域的范围大得多,而“装饰”则意味着,也远大于阴影边缘。我的怀疑是,这是为了避免改变实际窗口的大小以响应“可见”区域的范围,原因很简单,在复合环境中更改窗口大小是因为必须重新/初始化新的GPU资源的相当大的代价,这可能导致口吃和视觉伪影。为了让事情顺利运行--很明显-- Gnome显然采用了一种保守的资源重新分配策略,但是在X11方面,这可能会导致在查询窗口属性时出现不直观的值。

为了解决这个问题,我运行了一个由FVWM管理的urxvt和gnome控制中心的实例,并将其丢弃。事情就像这样(原始的SVG在这里找到:https://dl.datenwolf.net/xsvgtree/fvwm-gnome-shell.svg ):

正如你所看到的,在被修复的情况下,侏儒控制中心实际上会“表现”自己。另外,FVWM创建的所有小装饰元素都包含自己的小窗口,这是不值得的。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72899922

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档