admin管理员组

文章数量:1122846

I need a transparent X11 window that passes all mouse events. For example, it must let select a text in the window behind.

This is my code that makes the right half of the window transparent:

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <stdio.h>
#include <X11/extensions/shape.h>
#include <X11/extensions/Xfixes.h>

#define _NET_WM_STATE_REMOVE        0 
#define _NET_WM_STATE_ADD           1
#define _NET_WM_STATE_TOGGLE        2

Bool MakeAlwaysOnTop(Display* display,  Window mywin) {
    int screen = DefaultScreen(display);
    Window root = RootWindow(display, screen);
    Atom wmStateAbove = XInternAtom( display, "_NET_WM_STATE_ABOVE", 1 );
    if( wmStateAbove != None ) {
        printf( "_NET_WM_STATE_ABOVE has atom of %ld\n", (long)wmStateAbove );
    } else {
        printf( "ERROR: cannot find atom for _NET_WM_STATE_ABOVE !\n" );
        return False;
    }
    Atom wmNetWmState = XInternAtom( display, "_NET_WM_STATE", 1 );
    if( wmNetWmState != None ) {
        printf( "_NET_WM_STATE has atom of %ld\n", (long)wmNetWmState );
    } else {
        printf( "ERROR: cannot find atom for _NET_WM_STATE !\n" );
        return False;
    }
    if( wmStateAbove != None ) {
        XClientMessageEvent xclient;
        memset( &xclient, 0, sizeof (xclient) );
        xclient.type = ClientMessage;
        xclient.window = mywin;          
        xclient.message_type = wmNetWmState;
        xclient.format = 32;
        xclient.data.l[0] = _NET_WM_STATE_ADD;
        xclient.data.l[1] = wmStateAbove; 
        xclient.data.l[2] = 0;           
        xclient.data.l[3] = 0;
        xclient.data.l[4] = 0;
        XSendEvent( display,
          root,
          False,
          SubstructureRedirectMask | SubstructureNotifyMask,
          (XEvent *)&xclient );
        XFlush(display);
        return True;
    }
    return False;
}

const int width  = 400;
const int height = 300;

int main() {
    Display *dpy;
    Window win;
    GC gc;
    XEvent event;
    int screen;

    dpy = XOpenDisplay(NULL);
    if (dpy == NULL) {
        fprintf(stderr, "Cannot open display\n");
        return 1;
    }

    screen = DefaultScreen(dpy);
    win = XCreateSimpleWindow(dpy, RootWindow(dpy, screen), 10, 10, width, height, 1, BlackPixel(dpy, screen), WhitePixel(dpy, screen));

    Pixmap shape = XCreatePixmap(dpy, win, width, height, 1);
    GC gc2 = XCreateGC(dpy, shape, 0, NULL);
    XSetFillStyle(dpy, gc2, FillSolid);
    XSetForeground(dpy, gc2, 0);
    XFillRectangle(dpy, shape, gc2, 0, 0, width, height);
    XSetForeground(dpy, gc2, 1);
    XFillRectangle(dpy, shape, gc2, 0, 0, width / 2, height);
    XShapeCombineMask(dpy, win, ShapeBounding, 0, 0, shape, ShapeSet);

    XMapWindow(dpy, win);
    set_no_decoration(dpy, win);
    MakeAlwaysOnTop(dpy, win);
//    make_window_transparent(dpy, win);

    while (1) {
        XNextEvent(dpy, &event);
        if (event.type == KeyPress) {
            break;
        }
    }

    XFreeGC(dpy, gc);
    XDestroyWindow(dpy, win);
    XCloseDisplay(dpy);

    return 0;
}

void make_window_transparent(Display* display, Window window) {
    XRectangle rect;
    XserverRegion region = XFixesCreateRegion(display, &rect, 1);
    XFixesSetWindowShapeRegion(display, window, ShapeInput, 0, 0, region);
    XFixesDestroyRegion(display, region);
}

struct mwm_hints{
    int flags;
    int functions;
    int decorations;
    int input_mode;
    int status;
};

#define MWM_HINTS_DEC   (1 << 1)
#define MWM_DECOR_NONE  0

void set_no_decoration(Display* dpy, Window win) {
    Atom wm_hints;

    if((wm_hints = XInternAtom(dpy, "_MOTIF_WM_HINTS", True)) != None) {
        struct mwm_hints hints = {MWM_HINTS_DEC, 0, MWM_DECOR_NONE, 0, 0};
        XChangeProperty(dpy, win, wm_hints, wm_hints, 32, PropModeReplace, (unsigned char*)&hints, 4);
    }
}

And this is the result:

As you see, in the right (transparent) part I can select a text, so mouse events pass. The problem is that it works only if I call set_no_decoration function that removes title bar and borders.

But I need both title bar and borders. If I don't call set_no_decoration then I have title bar and borders, but mouse events don't pass through transparent part of the window - for example, it is not possible to select a text.

Could anyone say how to make a transparent window with title bar and border that passes mouse events?

本文标签: cHow to make a transparent window with title bar and border pass mouse eventsStack Overflow