XResizeWindow后,新绘制的窗口内容不显示

问题描述 投票:0回答:1

我正在 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_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;
        }
    }
}

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

linux resize window cairo xlib
1个回答
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;
            }
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.