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

jmp7cifd  于 2023-10-16  发布在  Linux
关注(0)|答案(1)|浏览(128)

我正在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;
        }
    }
}
  • 但还有一个问题 * 是否可以只使用一个线程获得相同的结果?
ttygqcqt

ttygqcqt1#

下面是您的代码的单线程版本。这不是很好,但它似乎工作。困难的部分是同时等待来自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;
            }
        }
    }
}

相关问题