【发布时间】:2025-12-31 17:15:02
【问题描述】:
我需要在 X11 中直接在屏幕上绘制一个形状(一个小箭头),它用作叠加层。我正在寻找大约一个小时没有好的结果。谁能为我提供一个很好的切入点来满足我的需求?我可以使用的技术是 cairo、gtk 或 XLib。
到目前为止,我发现的所有内容要么取决于并不总是可用的合成,要么会在我的箭头后面创建一个白色形状(矩形、窗口)。
编辑:我现在可以使用 Composite 和 Cairo 绘制 X11 Overlay。我是这样做的(注意:这是一个最小的例子。它几乎没有错误检查!!!)。从终端开始可以退出它!
// COMPILE WITH: g++ -o overlay overlay.cc -lXfixes -lXcomposite -lX11 `pkg-config --cflags --libs cairo`
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/Xcomposite.h>
#include <X11/extensions/Xfixes.h>
#include <X11/extensions/shape.h>
#include <cairo/cairo.h>
#include <cairo/cairo-xlib.h>
#include <stdio.h>
#include <unistd.h>
Display *display_;
int old_x_, old_y_;
cairo_surface_t *surf_;
Window overlay_;
int screen_;
int height_;
int width_;
cairo_t *cr_;
void paint_cursor(int new_x, int new_y, bool first = false)
{
if (!first)
{
cairo_set_operator(cr_, CAIRO_OPERATOR_CLEAR);
cairo_rectangle(cr_, old_x_, old_y_, 20, 20);
cairo_fill(cr_);
}
old_x_ = new_x;
old_y_ = new_y;
cairo_set_operator(cr_, CAIRO_OPERATOR_SOURCE);
cairo_move_to(cr_, new_x, new_y);
cairo_line_to(cr_, new_x + 0, new_y + 16);
cairo_line_to(cr_, new_x + 4, new_y + 13);
cairo_line_to(cr_, new_x + 7, new_y + 18);
cairo_line_to(cr_, new_x + 9, new_y + 17);
cairo_line_to(cr_, new_x + 6, new_y + 12);
cairo_line_to(cr_, new_x + 11, new_y + 12);
cairo_line_to(cr_, new_x + 0, new_y + 0);
cairo_set_source_rgba(cr_, 0.0, 0.0, 0.0, 0.5);
cairo_stroke_preserve(cr_);
cairo_set_source_rgba(cr_, 0.0, 1.0, 0.0, 0.5);
cairo_fill(cr_);
}
int main()
{
display_ = ::XOpenDisplay(0);
if (!display_)
{
return -1;
}
screen_ = ::XDefaultScreen(display_);
Window root = RootWindow(display_, screen_);
::XCompositeRedirectSubwindows(display_, root, CompositeRedirectAutomatic);
::XSelectInput(display_, root, SubstructureNotifyMask);
overlay_ = ::XCompositeGetOverlayWindow(display_, root);
XserverRegion region = ::XFixesCreateRegion(display_, 0, 0);
::XFixesSetWindowShapeRegion(display_, overlay_, ShapeBounding, 0, 0, 0);
::XFixesSetWindowShapeRegion(display_, overlay_, ShapeInput, 0, 0, region);
::XFixesDestroyRegion(display_, region);
width_ = DisplayWidth(display_, screen_);
height_ = DisplayHeight(display_, screen_);
surf_ = ::cairo_xlib_surface_create(display_, overlay_, DefaultVisual(display_, screen_), width_, height_);
cr_ = ::cairo_create(surf_);
::XSelectInput(display_, overlay_, ExposureMask);
old_x_ = 0;
old_y_ = 0;
paint_cursor(0, 0, true);
XEvent ev;
Window root_return, child_return;
int root_x_return, root_y_return;
int win_x_return, win_y_return;
unsigned int mask;
for (;;)
{
XQueryPointer(display_, root, &root_return, &child_return, &root_x_return, &root_y_return, &win_x_return, &win_y_return, &mask);
paint_cursor(root_x_return, root_y_return);
printf("Paint\n");
}
return 0;
}
剩下的问题:如何删除已绘制的旧光标?我尝试了 XClearArea,我尝试使用 Cairo 进行重绘,我尝试了 Cairo Clear 运算符,等等。没有任何效果。有人能指出我正确的方向吗?
【问题讨论】:
-
Core X11 不这样做,您将需要某种扩展。您可以使用复合或形状扩展。形状比复合更容易获得。如果可用,您还可以使用叠加视觉效果(需要硬件中的 OpenGL 和叠加支持)。
-
您也可以直接在现有窗口上绘制,但不建议这样做。如果其他程序同时尝试重绘,结果可能看起来不正确。
-
看xeyes的来源。古老但有效。
-
xeyes 只是一个没有绘制背景的简单窗口。我所做的是一个覆盖......
-
不,它是一个有形状的窗口,而不仅仅是一个没有背景的窗口。