-
-
Save ericek111/774a1661be69387de846f5f5a5977a46 to your computer and use it in GitHub Desktop.
/* | |
* Copyright (c) 2020 ericek111 <erik.brocko@letemsvetemapplem.eu>. | |
* | |
* 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; | |
} | |
Hey. Thanks for respecting the spirit of open-source! I've licensed the code under GPLv3. If that does not suit you, we can arrange something else, though GPL is preferred.
Awesome, thanks! GPL is fine.
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.
@ericek111 hi, would you be interested in adding an open-source license to this code? I'd like to base a project off this program, but without a license I can't legally do that.