I want to draw simple primitives at specific pixels on the screen (similar to this question). In order to do that I draw on top of all windows using the Overlay Window of the Window Manager. I can see the shape I am drawing and mouse events pass through but I don't see for example Window movements that are below the Overlay Window (unless I kill my application). I am new to Xlib programming, sry for asking a maybe simple question.
#include <assert.h>
#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/extensions/Xcomposite.h>
#include <X11/extensions/Xfixes.h>
#include <X11/extensions/shape.h>
#include <cairo.h>
#include <cairo-xlib.h>
Display *d;
Window overlay;
Window root;
int width, height;
void
allow_input_passthrough (Window w)
{
XserverRegion region = XFixesCreateRegion (d, NULL, 0);
XFixesSetWindowShapeRegion (d, w, ShapeBounding, 0, 0, 0);
XFixesSetWindowShapeRegion (d, w, ShapeInput, 0, 0, region);
XFixesDestroyRegion (d, region);
}
void
prep_overlay (void)
{
overlay = XCompositeGetOverlayWindow (d, root);
allow_input_passthrough (overlay);
}
void draw(cairo_t *cr) {
int quarter_w = width / 4;
int quarter_h = height / 4;
cairo_set_source_rgb(cr, 1.0, 0.0, 0.0);
cairo_rectangle(cr, quarter_w, quarter_h, quarter_w * 2, quarter_h * 2);
cairo_fill(cr);
}
int main() {
d = XOpenDisplay(NULL);
int s = DefaultScreen(d);
root = RootWindow(d, s);
XCompositeRedirectSubwindows (d, root, CompositeRedirectAutomatic);
XSelectInput (d, root, SubstructureNotifyMask);
width = DisplayWidth(d, s);
height = DisplayHeight(d, s);
prep_overlay();
cairo_surface_t *surf = cairo_xlib_surface_create(d, overlay,
DefaultVisual(d, s),
width, height);
cairo_t *cr = cairo_create(surf);
XSelectInput(d, overlay, ExposureMask);
draw(cr);
XEvent ev;
while (1) {
XNextEvent(d, &ev);
if (ev.type == Expose) {
draw(cr);
}
}
cairo_destroy(cr);
cairo_surface_destroy(surf);
XCloseDisplay(d);
return 0;
}
How can I draw pixels on the Overlay Window and still see the windows below?
May I suggest a simpler, pure X11 solution that does not have the flickering problem I experienced and also mentioned here. It uses the override_redirect functionality in Xlib:
#include <assert.h>
#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/X.h>
#include <X11/Xutil.h>
#include <cairo.h>
#include <cairo-xlib.h>
#include <chrono>
#include <thread>
void draw(cairo_t *cr) {
cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.5);
cairo_rectangle(cr, 0, 0, 200, 200);
cairo_fill(cr);
}
int main() {
Display *d = XOpenDisplay(NULL);
Window root = DefaultRootWindow(d);
int default_screen = XDefaultScreen(d);
// these two lines are really all you need
XSetWindowAttributes attrs;
attrs.override_redirect = true;
XVisualInfo vinfo;
if (!XMatchVisualInfo(d, DefaultScreen(d), 32, TrueColor, &vinfo)) {
printf("No visual found supporting 32 bit color, terminating\n");
exit(EXIT_FAILURE);
}
// these next three lines add 32 bit depth, remove if you dont need and change the flags below
attrs.colormap = XCreateColormap(d, root, vinfo.visual, AllocNone);
attrs.background_pixel = 0;
attrs.border_pixel = 0;
// Window XCreateWindow(
// Display *display, Window parent,
// int x, int y, unsigned int width, unsigned int height, unsigned int border_width,
// int depth, unsigned int class,
// Visual *visual,
// unsigned long valuemask, XSetWindowAttributes *attributes
// );
Window overlay = XCreateWindow(
d, root,
0, 0, 200, 200, 0,
vinfo.depth, InputOutput,
vinfo.visual,
CWOverrideRedirect | CWColormap | CWBackPixel | CWBorderPixel, &attrs
);
XMapWindow(d, overlay);
cairo_surface_t* surf = cairo_xlib_surface_create(d, overlay,
vinfo.visual,
200, 200);
cairo_t* cr = cairo_create(surf);
draw(cr);
XFlush(d);
std::this_thread::sleep_for(std::chrono::milliseconds(10000));
cairo_destroy(cr);
cairo_surface_destroy(surf);
XUnmapWindow(d, overlay);
XCloseDisplay(d);
return 0;
}
I went ahead and added 32 bit depth, but you get the picture. You can remove it if you desire.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With