【问题标题】:After XResizeWindow, new window content drawn is not displayedXResizeWindow 后,新绘制的窗口内容不显示
【发布时间】:2023-03-16 20:10:01
【问题描述】:

我正在 Linux 中编写一个应用程序,使用 Xlib 来管理一个窗口,并使用 cairo 在其中绘制一些文本。窗口的文本内容在执行过程中会发生变化,因此我想调整窗口大小以匹配文本范围的大小。如果文本范围的大小没有改变,则窗口始终会正确更新为新文本。

但是当文本范围发生变化时,窗口会相应地调整大小,窗口会被清除,但新文本永远不会显示。只有在没有调用 XResizeWindow 时才会实际显示文本。我使用的代码是

if (/* Text extent is changed */)
{
    XResizeWindow (display, window, new_width, new_height);
    cairo_xlib_surface_set_size (surface, new_width, new_height);
}

XClearWindow (display, window);

/* ... Cairo code to draw the text ... */

// cairo_surface_flush (surface);
// XFlush (display);

我还尝试在绘制文本的 Cairo 代码之后添加方法 cairo_surface_flushXFlush(在示例中注释),但没有任何变化。

编辑:我使用两个线程解决了这个问题:第一个线程具有用于监听 Expose 事件的常规循环加上重绘内容的代码和第二个线程发出调整窗口大小并发送 Expose 事件以唤醒第一个线程的线程。

在此示例中,窗口每 500 毫秒调整一次大小为随机宽度和高度,并且每次调整大小时都会在其中显示一个渐进式计数器。我使用 C++11,编译:

g++ -std=c++11 -o test test.cpp -lX11 -lcairo -lpthread

代码是:

#include <random>
#include <chrono>
#include <thread>
#include <string>
#include <X11/Xlib.h>
#include <cairo/cairo-xlib.h>

Display * d;
Window w;
cairo_surface_t * surface;
int width = 300, height = 300;
unsigned char counter = 0;
std::random_device rd;
std::knuth_b gen (rd ());
std::uniform_int_distribution < > dist (150, 300);

void logic ()
{
    XEvent send_event;
    send_event.type = Expose;
    send_event.xexpose.window = w;

    while (true)
    {
        std::this_thread::sleep_for (std::chrono::milliseconds (500));
        ++ counter;
        width = dist (gen);
        height = dist (gen);
        cairo_xlib_surface_set_size (surface, width, height);
        XResizeWindow (d, w, width, height);
        XSendEvent (d, w, False, ExposureMask, & send_event);
        XFlush (d);
    }
}

int main ( )
{
    XInitThreads ();

    d = XOpenDisplay (NULL);
    w = XCreateSimpleWindow (d, RootWindow (d, 0), 0, 0, width, height, 0, 0, 0x000000);
    XMapWindow (d, w);
    XSelectInput (d, w, ExposureMask | KeyPressMask);

    surface = cairo_xlib_surface_create (d, w, DefaultVisual (d, 0), width, height);
    cairo_t * cairo = cairo_create (surface);
    cairo_select_font_face (cairo, "FreeSans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
    cairo_set_font_size (cairo, 40 );
    cairo_set_source_rgb (cairo, 0.8, 0.8, 0.8);
    cairo_move_to (cairo, 40.0, 60.0);
    cairo_show_text (cairo, std::to_string (counter).c_str ());
    XFlush (d);

    std::thread T (logic);

    XEvent event;
    while (true)
    {
        XNextEvent (d, & event);
        if (event.type == Expose)
        {
            XClearWindow (d, w);
            cairo_move_to (cairo, 40.0, 60.0);
            cairo_show_text (cairo, std::to_string (counter).c_str ());
        }
        else if (event.type == KeyPress)
        {
            XCloseDisplay (d);
            return 0;
        }
    }
}

但还有一个问题:是否有可能只使用一个线程获得相同的结果?

【问题讨论】:

  • 您有可以测试的独立示例吗?你的事件循环是什么样子的,即你在 Expose 事件上做了什么?
  • 好的,我将提供一个紧凑的独立代码示例来重现该行为。

标签: linux resize window cairo xlib


【解决方案1】:

这是您的代码的单线程版本。这不是很好,但它似乎工作。困难的部分是同时等待来自 X11 服务器的事件和超时。我在以下代码中使用select() 执行此操作。 请注意,我还处理 ConfigureNotify 事件,而不是假设 XResizeWindow 总是做我们想要的。

#include <random>
#include <chrono>
#include <thread>
#include <string>
#include <X11/Xlib.h>
#include <cairo/cairo-xlib.h>
#include <sys/time.h>

Display * d;
Window w;
cairo_surface_t * surface;
int width = 300, height = 300;
unsigned char counter = 0;
std::random_device rd;
std::knuth_b gen (rd ());
std::uniform_int_distribution < > dist (150, 300);

void do_update ()
{
        ++ counter;
        width = dist (gen);
        height = dist (gen);
        XResizeWindow (d, w, width, height);
        // Force a redraw
        XClearArea(d, w, 0, 0, 0, 0, True);
}

int main ( )
{
    XInitThreads ();

    d = XOpenDisplay (NULL);
    w = XCreateSimpleWindow (d, RootWindow (d, 0), 0, 0, width, height, 0, 0, 0x000000);
    XMapWindow (d, w);
    XSelectInput (d, w, ExposureMask | KeyPressMask);

    surface = cairo_xlib_surface_create (d, w, DefaultVisual (d, 0), width, height);
    cairo_t * cairo = cairo_create (surface);
    cairo_select_font_face (cairo, "FreeSans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
    cairo_set_font_size (cairo, 40 );
    cairo_set_source_rgb (cairo, 0.8, 0.8, 0.8);
    cairo_move_to (cairo, 40.0, 60.0);
    cairo_show_text (cairo, std::to_string (counter).c_str ());
    XFlush (d);

    struct timeval next_update;
    struct timeval now;
    struct timeval interval = { 0, 500000 };
    gettimeofday(&now, NULL);
    timeradd(&now, &interval, &next_update);

    while (true)
    {
        XEvent event;

        gettimeofday(&now, NULL);
        if (timercmp(&now, &next_update, >)) {
            // Store time of next update
            timeradd(&now, &interval, &next_update);
            puts("update");
            do_update();
        }

        if (!XPending(d)) {
            struct timeval remaining;
            fd_set fds;
            int fd = ConnectionNumber(d);
            FD_ZERO(&fds);
            FD_SET(fd, &fds);
            timersub(&next_update, &now, &remaining);
            select(fd + 1, &fds, NULL, NULL, &remaining);
        } else {
            XNextEvent (d, & event);
            if (event.type == Expose)
            {
                XClearWindow (d, w);
                cairo_move_to (cairo, 40.0, 60.0);
                cairo_show_text (cairo, std::to_string (counter).c_str ());
            }
            if (event.type == ConfigureNotify)
            {
                cairo_xlib_surface_set_size (surface, event.xconfigure.width, event.xconfigure.height);
            }
            else if (event.type == KeyPress)
            {
                XCloseDisplay (d);
                return 0;
            }
        }
    }
}

【讨论】:

  • 代码很有意思,谢谢。几个问题:1) select 函数是仅用于等待和实现计时器,还是与等待显示器文件描述符上的非阻塞读取有关? 2)update()中使用的ClearArea函数是不是只是用来触发服务器发送Expose事件?
  • 1) 好吧... select() 用于“等到时间过去,但如果有任何事件到达,则立即处理来自服务器的所有事件”。 2)是的。 3)请注意,我没有以任何方式优化此代码。它必须在绘制后调用 cairo_surface_flush()。它应该只处理计数条目为零等的公开事件。可能还有很多其他问题。
  • 好的,明白了...但是我仍然对 X 服务器的工作有疑问,这实际上是我问题的根源。我不明白为什么,即使在客户端调用 XFlush 或 XSync 之后,服务器也不会在窗口上执行实际的绘图。似乎只有当客户端在通常的循环中使用 XNextEvent 读取 Expose 事件时,服务器才会真正尊重客户端的绘图请求。为什么是这样?服务器是否检查客户端是否读取了 Expose 事件,然后进行实际绘制?
  • 如何:在窗口上选择 SubstructureRedirect 事件。 ConfigureWindow 请求(这是 XResizeWindow 内部使用的)然后转换为 ConfigureRequest 事件并发送到 WM(否则被服务器忽略)。 WM 仍然可以发送 ConfigureWindow 请求而不被重定向。原因:WM 获取您的窗口并将其放置在自己的另一个窗口中。在此窗口中,它绘制例如标题栏(关闭按钮,...)。当“你的”窗口调整大小时,WM创建的窗口也需要调整大小。
  • 随机谷歌结果:seasonofcode.com/posts/…
猜你喜欢
  • 2021-03-01
  • 2017-09-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-09
  • 1970-01-01
  • 2012-10-28
相关资源
最近更新 更多