我正在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_flush 和 XFlush (在示例中注释),但没有任何变化。
编辑:我使用两个线程解决了问题:第一个带有通常循环的线程用于侦听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;
}
}
}
但问题仍然存在:是否可以仅使用一个帖子获得相同的结果?
答案 0 :(得分: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;
}
}
}
}