-
-
Save ericek111/774a1661be69387de846f5f5a5977a46 to your computer and use it in GitHub Desktop.
| /* | |
| * Copyright (c) 2020 ericek111 <[email protected]>. | |
| * | |
| * This program is free software: you can redistribute it and/or modify | |
| * it under the terms of the GNU General Public License as published by | |
| * the Free Software Foundation, version 3. | |
| * | |
| * This program is distributed in the hope that it will be useful, but | |
| * WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU General Public License | |
| * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| */ | |
| #include <iostream> | |
| #include <stdlib.h> | |
| #include <climits> | |
| #include <chrono> | |
| #include <X11/Xos.h> | |
| #include <X11/Xlib.h> | |
| #include <X11/Xutil.h> | |
| #include <X11/Xatom.h> | |
| #include <X11/extensions/shape.h> | |
| #include <X11/extensions/Xcomposite.h> | |
| #include <X11/extensions/Xfixes.h> | |
| #include <math.h> | |
| // Events for normal windows | |
| #define BASIC_EVENT_MASK (StructureNotifyMask|ExposureMask|PropertyChangeMask|EnterWindowMask|LeaveWindowMask|KeyPressMask|KeyReleaseMask|KeymapStateMask) | |
| #define NOT_PROPAGATE_MASK (KeyPressMask|KeyReleaseMask|ButtonPressMask|ButtonReleaseMask|PointerMotionMask|ButtonMotionMask) | |
| using namespace std; | |
| Display *g_display; | |
| int g_screen; | |
| Window g_win; | |
| int g_disp_width; | |
| int g_disp_height; | |
| Pixmap g_bitmap; | |
| Colormap g_colormap; | |
| XColor red; | |
| XColor black; | |
| XColor white; | |
| std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now(); | |
| std::chrono::high_resolution_clock::time_point t2 = std::chrono::high_resolution_clock::now(); | |
| int fpsmeterc = 0; | |
| #define FPSMETERSAMPLE 100 | |
| auto duration = std::chrono::duration_cast<std::chrono::microseconds>( t2 - t1 ).count(); | |
| string fpsstring = ""; | |
| int shape_event_base; | |
| int shape_error_base; | |
| // The window size | |
| int WIDTH = 1920; | |
| int HEIGHT = 1080; | |
| long event_mask = (StructureNotifyMask|ExposureMask|PropertyChangeMask|EnterWindowMask|LeaveWindowMask|KeyRelease | ButtonPress|ButtonRelease|KeymapStateMask); | |
| void allow_input_passthrough (Window w) { | |
| XserverRegion region = XFixesCreateRegion (g_display, NULL, 0); | |
| //XFixesSetWindowShapeRegion (g_display, w, ShapeBounding, 0, 0, 0); | |
| XFixesSetWindowShapeRegion (g_display, w, ShapeInput, 0, 0, region); | |
| XFixesDestroyRegion (g_display, region); | |
| } | |
| void list_fonts() { | |
| char **fontlist; | |
| int num_fonts; | |
| fontlist = XListFonts (g_display, "*", 1000, &num_fonts); | |
| for (int i = 0; i < num_fonts; ++i) { | |
| fprintf(stderr, "> %s\n", fontlist[i]); | |
| } | |
| } | |
| // Create a XColor from 3 byte tuple (0 - 255, 0 - 255, 0 - 255). | |
| XColor createXColorFromRGB(short red, short green, short blue) { | |
| XColor color; | |
| // m_color.red = red * 65535 / 255; | |
| color.red = (red * 0xFFFF) / 0xFF; | |
| color.green = (green * 0xFFFF) / 0xFF; | |
| color.blue = (blue * 0xFFFF) / 0xFF; | |
| color.flags = DoRed | DoGreen | DoBlue; | |
| if (!XAllocColor(g_display, DefaultColormap(g_display, g_screen), &color)) { | |
| std::cerr << "createXColorFromRGB: Cannot create color" << endl; | |
| exit(-1); | |
| } | |
| return color; | |
| } | |
| // Create a XColor from 3 byte tuple (0 - 255, 0 - 255, 0 - 255). | |
| XColor createXColorFromRGBA(short red, short green, short blue, short alpha) { | |
| XColor color; | |
| // m_color.red = red * 65535 / 255; | |
| color.red = (red * 0xFFFF) / 0xFF; | |
| color.green = (green * 0xFFFF) / 0xFF; | |
| color.blue = (blue * 0xFFFF) / 0xFF; | |
| color.flags = DoRed | DoGreen | DoBlue; | |
| if (!XAllocColor(g_display, DefaultColormap(g_display, g_screen), &color)) { | |
| std::cerr << "createXColorFromRGB: Cannot create color" << endl; | |
| exit(-1); | |
| } | |
| *(&color.pixel) = ((*(&color.pixel)) & 0x00ffffff) | (alpha << 24); | |
| return color; | |
| } | |
| // Create a window | |
| void createShapedWindow() { | |
| XSetWindowAttributes wattr; | |
| XColor bgcolor = createXColorFromRGBA(0, 0, 0, 0); | |
| Window root = DefaultRootWindow(g_display); | |
| Visual *visual = DefaultVisual(g_display, g_screen); | |
| XVisualInfo vinfo; | |
| XMatchVisualInfo(g_display, DefaultScreen(g_display), 32, TrueColor, &vinfo); | |
| g_colormap = XCreateColormap(g_display, DefaultRootWindow(g_display), vinfo.visual, AllocNone); | |
| XSetWindowAttributes attr; | |
| attr.background_pixmap = None; | |
| attr.background_pixel = bgcolor.pixel; | |
| attr.border_pixel=0; | |
| attr.win_gravity=NorthWestGravity; | |
| attr.bit_gravity=ForgetGravity; | |
| attr.save_under=1; | |
| attr.event_mask=BASIC_EVENT_MASK; | |
| attr.do_not_propagate_mask=NOT_PROPAGATE_MASK; | |
| attr.override_redirect=1; // OpenGL > 0 | |
| attr.colormap = g_colormap; | |
| //unsigned long mask = CWBackPixel|CWBorderPixel|CWWinGravity|CWBitGravity|CWSaveUnder|CWEventMask|CWDontPropagate|CWOverrideRedirect; | |
| unsigned long mask = CWColormap | CWBorderPixel | CWBackPixel | CWEventMask | CWWinGravity|CWBitGravity | CWSaveUnder | CWDontPropagate | CWOverrideRedirect; | |
| g_win = XCreateWindow(g_display, root, 0, 0, WIDTH, HEIGHT, 0, vinfo.depth, InputOutput, vinfo.visual, mask, &attr); | |
| g_bitmap = XCreateBitmapFromData (g_display, RootWindow(g_display, g_screen), (char *)myshape_bits, myshape_width, myshape_height); | |
| //XShapeCombineMask(g_display, g_win, ShapeBounding, 900, 500, g_bitmap, ShapeSet); | |
| XShapeCombineMask(g_display, g_win, ShapeInput, 0, 0, None, ShapeSet ); | |
| // We want shape-changed event too | |
| #define SHAPE_MASK ShapeNotifyMask | |
| XShapeSelectInput (g_display, g_win, SHAPE_MASK ); | |
| // Tell the Window Manager not to draw window borders (frame) or title. | |
| wattr.override_redirect = 1; | |
| XChangeWindowAttributes(g_display, g_win, CWOverrideRedirect, &wattr); | |
| allow_input_passthrough(g_win); | |
| // Show the window | |
| XMapWindow(g_display, g_win); | |
| red = createXColorFromRGBA(255, 0, 0, 255); | |
| black = createXColorFromRGBA(0, 0, 0, 200); | |
| white = createXColorFromRGBA(255, 255, 255, 255); | |
| } | |
| // Draw on the shaped window. | |
| // Yes it's possible, but only pixels that hits the mask are visible. | |
| // A hint: You can change the mask during runtime if you like. | |
| void draw() | |
| { | |
| fpsmeterc++; | |
| if(fpsmeterc == FPSMETERSAMPLE) { | |
| fpsmeterc = 0; | |
| t1 = t2; | |
| t2 = std::chrono::high_resolution_clock::now(); | |
| duration = std::chrono::duration_cast<std::chrono::microseconds>( t2 - t1 ).count(); | |
| fpsstring = /*to_string(duration) + " / " +*/ to_string(1000000*FPSMETERSAMPLE/duration); | |
| } | |
| GC gc; | |
| XGCValues gcv; | |
| /*// Line width and type | |
| gcv.line_width = 1; | |
| gcv.line_style = LineSolid; | |
| gcv.foreground = red.pixel; | |
| unsigned long mask = GCLineWidth | GCLineStyle | GCForeground; | |
| gc = XCreateGC(g_display, g_win, mask, &gcv);*/ | |
| gc = XCreateGC (g_display, g_win, 0, 0); | |
| XSetBackground (g_display, gc, white.pixel); | |
| XSetForeground (g_display, gc, red.pixel); | |
| XFontStruct * font; | |
| // const char * fontname = "-misc-fixed-bold-r-normal--18-120-100-100-c-90-iso8859-2"; | |
| // const char * fontname = "rk24"; // ~ chinese shit | |
| // list_fonts(); | |
| const char * fontname = "9x15bold"; | |
| font = XLoadQueryFont (g_display, fontname); | |
| /* If the font could not be loaded, revert to the "fixed" font. */ | |
| if (!font) { | |
| fprintf (stderr, "unable to load font %s > using fixed\n", fontname); | |
| font = XLoadQueryFont (g_display, "fixed"); | |
| } | |
| XSetFont (g_display, gc, font->fid); | |
| XSetForeground (g_display, gc, black.pixel); | |
| XFillRectangle(g_display, g_win, gc, 0, 0, 250, 30); | |
| XSetForeground (g_display, gc, red.pixel); | |
| if(duration > 0.0f) { | |
| const char * text = fpsstring.c_str(); | |
| XDrawString(g_display, g_win, gc, 10, 20, text, strlen(text)); | |
| } | |
| XFreeGC(g_display, gc); | |
| } | |
| void openDisplay() { | |
| g_display = XOpenDisplay(0); | |
| if (!g_display) { | |
| cerr << "Failed to open X display" << endl; | |
| exit(-1); | |
| } | |
| g_screen = DefaultScreen(g_display); | |
| g_disp_width = DisplayWidth(g_display, g_screen); | |
| g_disp_height = DisplayHeight(g_display, g_screen); | |
| // Has shape extions? | |
| if (!XShapeQueryExtension (g_display, &shape_event_base, &shape_error_base)) { | |
| cerr << "NO shape extension in your system !" << endl; | |
| exit (-1); | |
| } | |
| } | |
| int main() { | |
| openDisplay(); | |
| createShapedWindow(); | |
| XEvent xevt; | |
| XExposeEvent *eev; | |
| XConfigureEvent *cev; | |
| XKeyEvent *kev; | |
| while (1) | |
| { | |
| /*XNextEvent(g_display, &xevt); | |
| // Note! Shaped window generates some special events | |
| // You got "shape_event_base" from XShapeQueryExtension(...) | |
| if (xevt.type == shape_event_base + ShapeNotify) | |
| { | |
| cout << "Got shape changed event" << endl; | |
| continue; | |
| } | |
| switch (xevt.type) | |
| { | |
| case Expose: | |
| if (xevt.xexpose.count != 0) continue; | |
| eev = &xevt.xexpose; | |
| draw(); | |
| break; | |
| case KeyPress: | |
| kev = &xevt.xkey; | |
| exit(0); | |
| break; | |
| case ConfigureNotify: | |
| cev = &xevt.xconfigure; | |
| break; | |
| }*/ | |
| draw(); | |
| usleep(1000); | |
| } | |
| return 0; | |
| } | |
xdrawoverlay.c: In function ‘void createShapedWindow()’:
xdrawoverlay.c:145:88: error: ‘myshape_bits’ was not declared in this scope
145 | g_bitmap = XCreateBitmapFromData (g_display, RootWindow(g_display, g_screen), (char *)myshape_bits, myshape_width, myshape_height);
| ^~~~~~~~~~~~
xdrawoverlay.c:145:102: error: ‘myshape_width’ was not declared in this scope
145 | g_bitmap = XCreateBitmapFromData (g_display, RootWindow(g_display, g_screen), (char *)myshape_bits, myshape_width, myshape_height);
| ^~~~~~~~~~~~~
xdrawoverlay.c:145:117: error: ‘myshape_height’ was not declared in this scope
145 | g_bitmap = XCreateBitmapFromData (g_display, RootWindow(g_display, g_screen), (char *)myshape_bits, myshape_width, myshape_height);
| ^~~~~~~~~~~~~~
@liangqi g_bitmap is not used anywhere, so you can remove lines 145 and 41 without problem, then compile with
g++ overlay.cpp -o overlay -lX11 -lXext -lm -lXfixes
Thanks for a very helpful example. I think before line 221 (XFreeGC()) you need:
XFreeFont(g_display, font);
to avoid leaking the font each time the draw loop is called.
This has been extremely useful. I need an external overlay for a program I'm making, and it has been almost impossible to make a X window which does exactly this.
I'd give it a star if it wasn't a gist.
In case anyone is interested, here is a C fork with some extra stuff: Link.
Awesome, thanks! GPL is fine.