Last active
July 20, 2019 18:02
-
-
Save sdaau/867804ad4b7d05cd926179f03285d40e to your computer and use it in GitHub Desktop.
xosd_track_cursor.c
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* gcc -o xosd_track_cursor xosd_track_cursor.c $(pkg-config --cflags --libs xi x11 xext cairo xfixes gl) -pthread */ | |
// https://keithp.com/blogs/Cursor_tracking/ | |
#include <stdio.h> | |
#include <string.h> | |
#include <GL/gl.h> | |
#include <GL/glx.h> | |
//~ #include <GL/glxext.h> //glXSwapIntervalSGI | |
#include <X11/Xlib.h> | |
#include <X11/extensions/XInput2.h> | |
// from xosd.c (via https://github.com/AndreRenaud/XOSD) | |
#include <stdarg.h> | |
//#include <stdio.h> | |
#include <stdlib.h> | |
//#include <string.h> | |
#include <unistd.h> | |
#include <semaphore.h> | |
#include <signal.h> | |
#include <assert.h> | |
#include <pthread.h> | |
#include <X11/Xlib.h> | |
#include <X11/Xutil.h> | |
#include <X11/extensions/shape.h> | |
#include <X11/Xatom.h> | |
#define MUTEX_GET() sem_wait (&osd->mutex) | |
#define MUTEX_RELEASE() sem_post (&osd->mutex) | |
//https://stackoverflow.com/questions/7165408/how-to-draw-directly-on-the-desktop | |
//~ #define TRY_CAIRO | |
#ifdef TRY_CAIRO | |
#include <cairo.h> | |
#include <cairo-xlib.h> | |
#endif | |
// [How to get cursor/mouse-pointer image (pixbuf) for an application window.](https://ubuntuforums.org/showthread.php?t=1418371) | |
//https://stackoverflow.com/questions/55135400/issue-compiling-file-with-x11 | |
//https://github.com/dnschneid/crouton/blob/master/src/fbserver.c | |
#include <X11/extensions/Xfixes.h> | |
#define WINDOW_WIDTH 60 | |
#define WINDOW_HEIGHT WINDOW_WIDTH | |
//~ #define DEBUGPRINT | |
//~ #define MOUSE_TRAIL | |
typedef struct pnt { | |
int x; | |
int y; | |
} pnt_t; | |
struct xosd { | |
pthread_t event_thread; | |
pthread_t timeout_thread; | |
sem_t mutex; | |
Display *display; | |
int screen; | |
Window window; | |
unsigned int depth; | |
Pixmap bitmap; | |
Visual *visual; | |
GC gc; | |
GC bitmap_gc; | |
GC bitmap_gc_back; | |
GC transp_gc; | |
int width; | |
int height; | |
int x; | |
int y; | |
int offset; | |
int shadow_offset; | |
int mapped; | |
int done; | |
unsigned int pixel; | |
XColor colour; | |
Colormap colourmap; | |
int timeout; | |
int timeout_time; | |
int mouse_root_x; | |
int mouse_root_y; | |
int is_exposing; | |
XWindowAttributes orig_attr; | |
XSetWindowAttributes transp_attr; | |
int fixesEvent; | |
int cursor_updated; | |
long int cursor_serial; | |
char cursor_name[256]; | |
XFixesCursorImage *cursor_image; | |
XImage *cursor_ximage; | |
XImage *cursor_ximageB; | |
GLXContext glcontext; | |
unsigned char buttonstate; | |
pnt_t mousehistpnts[2]; | |
#ifdef TRY_CAIRO | |
cairo_t* cr; | |
#endif | |
}; | |
typedef struct xosd xosd; | |
// http://www.cs.cityu.edu.hk/~lwang/ccs5253/XLibAnimation.htm | |
unsigned long GetColor( Display* dis, char* color_name ) | |
{ | |
Colormap cmap; | |
XColor near_color, true_color; | |
cmap = DefaultColormap( dis, 0 ); | |
XAllocNamedColor( dis, cmap, color_name, &near_color, &true_color ); | |
return( near_color.pixel ); | |
} | |
int xosd_hide (xosd *osd) { | |
assert (osd); | |
if (osd->mapped) | |
{ | |
MUTEX_GET (); | |
osd->mapped = 0; | |
XUnmapWindow (osd->display, osd->window); | |
XFlush (osd->display); | |
MUTEX_RELEASE (); | |
} | |
return 0; | |
} | |
int xosd_show (xosd *osd) { | |
assert (osd); | |
if (!osd->mapped) | |
{ | |
MUTEX_GET (); | |
osd->mapped = 1; | |
XMapRaised (osd->display, osd->window); | |
XFlush (osd->display); | |
MUTEX_RELEASE (); | |
} | |
return 0; | |
} | |
static void expose (xosd *osd) | |
{ | |
int line; | |
int x, y; | |
// XFontSetExtents *extents; | |
MUTEX_GET (); | |
//~ osd->is_exposing = 1; | |
#ifdef DEBUGPRINT | |
printf("expose (osd redraw)\n"); | |
#endif | |
XClearWindow(osd->display, osd->window); // does not delete bitmap draws | |
//~ glDrawBuffer(GL_BACK); | |
//~ glClearColor( 0.1, 0.1, 0.1, 0.1) ; | |
//~ glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT) ; | |
//~ glXSwapBuffers(osd->display, osd->window) ; // no clear without this - but messes further draws! | |
//~ glXWaitGL() ; // same as glFinish()? | |
//~ glXWaitX() ; | |
// from http://code.arp242.net/find-cursor | |
int size = 60; //320; | |
// this basically blanks the previous state of fillarc ellipse/circle (but not other drawn pixels): | |
XFillRectangle (osd->display, osd->bitmap, osd->bitmap_gc_back, | |
0, 0, osd->width, osd->height); | |
XFillRectangle (osd->display, osd->window, osd->transp_gc, | |
0, 0, osd->width, osd->height); | |
int destx=-1; | |
int desty=-1; | |
//~ int cursor_width = -1, cursorheight = -1; | |
if (osd->cursor_ximage) { | |
// http://www.dis.uniroma1.it/~liberato/screensaver/trans.html | |
//~ Pixmap pix = XCreatePixmap(osd->display, osd->window, osd->cursor_image->width, osd->cursor_image->height, osd->depth); | |
//~ GC pixgc = XCreateGC (osd->display, pix, 0, NULL); | |
//~ XPutImage(osd->display, pix, pixgc, osd->cursor_ximage, 0, 0, 0, 0, osd->cursor_image->width, osd->cursor_image->height); | |
//~ XSetClipMask(osd->display, osd->gc, pix); // BadMatch | |
//~ XSetClipOrigin(osd->display, osd->gc, 0, 0); | |
destx = (osd->width/2)-osd->cursor_image->xhot; | |
desty = (osd->height/2)-osd->cursor_image->yhot; | |
XPutImage(osd->display, osd->window, osd->gc, osd->cursor_ximage, 0, 0, 0+destx, 0+desty, osd->cursor_image->width, osd->cursor_image->height); | |
} | |
// NOTE: just with 32-bit depth, drawing with XPutImage with transparent background on top of previous, also erases previous pixels - there is no compositing/blending! | |
#ifdef MOUSE_TRAIL | |
#define MANUAL_ALPHA_BLENDING | |
#ifndef MANUAL_ALPHA_BLENDING | |
if (osd->cursor_ximageB) { | |
XPutImage(osd->display, osd->window, osd->gc, osd->cursor_ximageB, 0, 0, 0+destx+10, 0+desty+10, osd->cursor_image->width, osd->cursor_image->height); | |
XPutImage(osd->display, osd->window, osd->gc, osd->cursor_ximageB, 0, 0, 0+destx+5, 0+desty+5, osd->cursor_image->width, osd->cursor_image->height); | |
} | |
#else | |
if (osd->cursor_ximageB) { | |
XImage* tmp_ximage = XGetImage(osd->display, osd->window, 0, 0, osd->width, osd->height, AllPlanes, ZPixmap); | |
unsigned char r,g,b,a; | |
unsigned short row,col,pos; | |
unsigned short offs1x = destx+osd->mousehistpnts[0].x, offs1y = desty+osd->mousehistpnts[0].y, offs2x = destx+osd->mousehistpnts[1].x, offs2y = desty+osd->mousehistpnts[1].y; | |
for(pos = row = 0;row<osd->height; row++) | |
{ | |
for(col=0;col < osd->width;col++,pos++) | |
{ | |
unsigned long srcPixel = XGetPixel(tmp_ximage, col, row); | |
a = (unsigned char)((srcPixel >> 24) & 0xff); | |
r = (unsigned char)((srcPixel >> 16) & 0xff); | |
g = (unsigned char)((srcPixel >> 8) & 0xff); | |
b = (unsigned char)((srcPixel >> 0) & 0xff); | |
// must cast, else defaults at intefer division, so we get only 0.0 or 1.0! | |
float fa=(float)a/0xff, fr=(float)r/0xff, fg=(float)g/0xff, fb=(float)b/0xff; | |
unsigned long pixel1 = 0; | |
if ( ((row >= offs1x) && (row < offs1x+osd->cursor_ximageB->width)) && ((col >= offs1y) && (col < offs1y+osd->cursor_ximageB->height)) ) { | |
unsigned short adj1x = row - offs1x, adj1y = col - offs1y; | |
unsigned char r1,g1,b1; unsigned int a1,ia1; | |
pixel1 = XGetPixel(osd->cursor_ximageB, adj1y, adj1x); | |
a1 = (unsigned int)((pixel1 >> 24) & 0xff); | |
r1 = (unsigned char)((pixel1 >> 16) & 0xff); | |
g1 = (unsigned char)((pixel1 >> 8) & 0xff); | |
b1 = (unsigned char)((pixel1 >> 0) & 0xff); | |
//~ ia1 = 0xff - a1; | |
//~ r = (unsigned char)((a1*r1+ia1*r)>>8+1); | |
//~ g = (unsigned char)((a1*g1+ia1*g)>>8+1); | |
//~ b = (unsigned char)((a1*b1+ia1*b)>>8+1); | |
//~ a = a1; //(unsigned char)((a*a1)>>8+1); | |
float fa1=(float)a1/0xff, fr1=(float)r1/0xff, fg1=(float)g1/0xff, fb1=(float)b1/0xff; | |
float fouta = fa+fa1*(1.0-fa); | |
//~ float fouta = fa1+fa*(1.0-fa1); | |
//~ float fouta = (fa*(1.0-fa1)+fa1*(1.0-fa))/2; | |
if (fouta > 0.0) { | |
fr = (fr*fa+fr1*fa1*(1.0-fa))/fouta; | |
fg = (fg*fa+fg1*fa1*(1.0-fa))/fouta; | |
fb = (fb*fa+fb1*fa1*(1.0-fa))/fouta; | |
} else fr = fg = fb = 0.0; | |
r = (unsigned char)(fr*0xff); | |
g = (unsigned char)(fg*0xff); | |
b = (unsigned char)(fb*0xff); | |
a = (unsigned char)(fouta*0xff); //(unsigned char)((a*a1)>>8+1); | |
//~ printf("fouta %f a %d a1 %d fa %f fa1 %f\n", fouta, a, a1, fa, fa1); | |
} | |
unsigned long pixel2 = 0; | |
if ( ((row >= offs2x) && (row < offs2x+osd->cursor_ximageB->width)) && ((col >= offs2y) && (col < offs2y+osd->cursor_ximageB->height)) ) { | |
unsigned short adj2x = row - offs2x, adj2y = col - offs2y; | |
unsigned char r2,g2,b2; unsigned int a2,ia2; | |
pixel2 = XGetPixel(osd->cursor_ximageB, adj2y, adj2x); | |
a2 = (unsigned int)((pixel2 >> 24) & 0xff); | |
r2 = (unsigned char)((pixel2 >> 16) & 0xff); | |
g2 = (unsigned char)((pixel2 >> 8) & 0xff); | |
b2 = (unsigned char)((pixel2 >> 0) & 0xff); | |
fa=(float)a/0xff; // reset this one! | |
float fa2=(float)a2/0xff, fr2=(float)r2/0xff, fg2=(float)g2/0xff, fb2=(float)b2/0xff; | |
float fouta = fa+fa2*(1.0-fa); | |
//~ float fouta = fa2+fa*(1.0-fa2); | |
//~ float fouta = (fa*(1.0-fa2)+fa2*(1.0-fa))/2; | |
if (fouta > 0.0) { | |
fr = (fr*fa+fr2*fa2*(1.0-fa))/fouta; | |
fg = (fg*fa+fg2*fa2*(1.0-fa))/fouta; | |
fb = (fb*fa+fb2*fa2*(1.0-fa))/fouta; | |
} else fr = fg = fb = 0.0; | |
r = (unsigned char)(fr*0xff); | |
g = (unsigned char)(fg*0xff); | |
b = (unsigned char)(fb*0xff); | |
a = (unsigned char)(fouta*0xff); | |
//~ printf("fouta %f a %d a2 %d fa %f fa2 %f\n", fouta, a, a2, fa, fa2); | |
} | |
unsigned long out_pixel = 0; | |
out_pixel = ((a << 24) | (r << 16) | (g << 8) | b); | |
//~ out_pixel = srcPixel + pixel1; | |
XPutPixel(tmp_ximage, col, row, out_pixel); | |
} | |
} | |
XPutImage(osd->display, osd->window, osd->gc, tmp_ximage, 0, 0, 0, 0, osd->width, osd->height); | |
XDestroyImage(tmp_ximage); | |
} | |
#endif | |
#endif // MOUSE_TRAIL | |
XFillArc(osd->display, osd->bitmap, osd->bitmap_gc, | |
//20, 20, // x, y position | |
0, 0, // x, y position | |
WINDOW_WIDTH, WINDOW_HEIGHT, // Size | |
0, 360 * 64); // Make it a full circle | |
// hole | |
//XFillRectangle (osd->display, osd->bitmap, osd->bitmap_gc_back, | |
// WINDOW_WIDTH/2-1, WINDOW_HEIGHT/2-1, 3, 3); | |
#ifdef TRY_CAIRO | |
// try cairo draw | |
int cairo_x = x+10, cairo_y = y+10, cairo_width = 10, cairo_height = 10; | |
// first blank/clear... | |
// https://stackoverflow.com/questions/35052368/how-to-clean-cairos-surface-area-without-png-image | |
cairo_operator_t old_cairo_op = cairo_get_operator(osd->cr); | |
cairo_set_source_rgba(osd->cr, 0.0, 0.0, 0.0, 0.0); | |
cairo_set_operator(osd->cr, CAIRO_OPERATOR_CLEAR); | |
cairo_rectangle(osd->cr, 0, 0, osd->width, osd->height); | |
cairo_paint_with_alpha(osd->cr, 0.0); | |
// ... then draw... | |
printf("%d %d -- %d %d\n", cairo_x, cairo_y, cairo_width, cairo_height); | |
if ((cairo_x < osd->width) && (cairo_y < osd->height)) { | |
cairo_set_operator(osd->cr, old_cairo_op); | |
//~ cairo_set_source_rgb(osd->cr, 1.0, 0.0, 0.0); | |
cairo_set_source_rgba(osd->cr, 1.0, 0.0, 0.0, 0.8); | |
cairo_rectangle(osd->cr, cairo_x, cairo_y, cairo_height, cairo_width); | |
cairo_fill(osd->cr); | |
} | |
#endif | |
// without this, no window cutout with ellipse, only main rectangular cut and limerect are visible | |
XShapeCombineMask (osd->display, osd->window, | |
ShapeBounding, | |
0, 0, | |
osd->bitmap, | |
ShapeSet); | |
XFlush (osd->display); | |
glXWaitX() ; | |
//~ osd->is_exposing = 0; | |
MUTEX_RELEASE (); | |
} | |
static int force_redraw (xosd *osd) { | |
assert (osd); | |
// seems XMoveWindow can go either before or after expose - doesn't matter, expose draws relative to window 0,0 | |
// centered over hole, can pick up mouse overs from below (but also can if I do combine shape) | |
XMoveWindow (osd->display, osd->window, osd->mouse_root_x-osd->width/2, osd->mouse_root_y-osd->height/2); // does not necessarrily cause Expose event! | |
expose (osd); | |
if (!osd->mapped) | |
{ | |
MUTEX_GET (); | |
XMapRaised (osd->display, osd->window); | |
osd->mapped = 1; | |
MUTEX_RELEASE (); | |
} | |
return 0; | |
} | |
static void *event_loop (void *osdv) { | |
xosd *osd = osdv; | |
XEvent report; | |
while (!osd->done) { | |
//~ usleep(1000000); | |
/* XCheckIfEvent (osd->display, &report, */ | |
/* XNextEvent(osd->display, &report); */ | |
MUTEX_GET (); | |
if (!XCheckWindowEvent (osd->display, osd->window, ExposureMask, &report)) | |
{ | |
MUTEX_RELEASE (); | |
usleep (500); | |
continue; | |
} | |
MUTEX_RELEASE (); | |
printf("event_loop "); | |
report.type &= 0x7f; /* remove the sent by server/manual send flag */ | |
switch (report.type) | |
{ | |
case Expose : | |
{ | |
printf (" (init) Expose event\n"); | |
//~ if (report.xexpose.count != 0) | |
//~ break; | |
//~ if (osd->is_exposing == 0) | |
//~ expose (osd); | |
break; | |
} | |
default: | |
printf (" %d\n", report.type); | |
} | |
} | |
return NULL; | |
} | |
static void *timeout_loop (void *osdv) { | |
xosd *osd = osdv; | |
assert (osd); | |
while (!osd->done) | |
{ | |
usleep (120000); | |
int do_expose = 0; | |
MUTEX_GET (); | |
#ifdef DEBUGPRINT | |
printf ("timeout_loop\n"); | |
#endif | |
#ifdef MOUSE_TRAIL | |
int factor = 99; | |
if (osd->mousehistpnts[0].x != 0) { | |
osd->mousehistpnts[0].x = osd->mousehistpnts[0].x * factor / 100; | |
do_expose = 1; | |
if (!(osd->mousehistpnts[0].x > 0)) { | |
osd->mousehistpnts[0].x = 0; | |
} | |
} | |
if (osd->mousehistpnts[0].y != 0) { | |
osd->mousehistpnts[0].y = osd->mousehistpnts[0].y * factor / 100; | |
do_expose = 1; | |
if (!(osd->mousehistpnts[0].y > 0)) { | |
osd->mousehistpnts[0].y = 0; | |
} | |
} | |
if (osd->mousehistpnts[1].x != 0) { | |
osd->mousehistpnts[1].x = osd->mousehistpnts[1].x * factor / 100; | |
do_expose = 1; | |
if (!(osd->mousehistpnts[1].x > 0)) { | |
osd->mousehistpnts[1].x = 0; | |
} | |
} | |
if (osd->mousehistpnts[1].y != 0) { | |
osd->mousehistpnts[1].y = osd->mousehistpnts[1].y * factor / 100; | |
do_expose = 1; | |
if (!(osd->mousehistpnts[1].y > 0)) { | |
osd->mousehistpnts[1].y = 0; | |
} | |
}; | |
#ifdef DEBUGPRINT | |
printf("mousehistpnts %d %d %d %d\n", osd->mousehistpnts[0].x, osd->mousehistpnts[0].y, osd->mousehistpnts[1].x, osd->mousehistpnts[1].y); | |
#endif | |
#endif //MOUSE_TRAIL | |
MUTEX_RELEASE (); | |
if (do_expose == 1) expose(osd); | |
} | |
return NULL; | |
} | |
static int set_colour (xosd *osd, char *colour) { | |
assert (osd); | |
MUTEX_GET (); | |
osd->colourmap = DefaultColormap (osd->display, osd->screen); | |
if (XParseColor (osd->display, osd->colourmap, colour, &osd->colour)) { | |
if (XAllocColor(osd->display, osd->colourmap, &osd->colour)) { | |
osd->pixel = osd->colour.pixel; | |
} else { | |
osd->pixel = WhitePixel(osd->display, osd->screen); | |
} | |
} else { | |
osd->pixel = WhitePixel(osd->display, osd->screen); | |
} | |
XSetForeground (osd->display, osd->gc, osd->pixel); | |
XSetBackground (osd->display, osd->gc, | |
WhitePixel (osd->display, osd->screen)); | |
MUTEX_RELEASE (); | |
return 0; | |
} | |
static int set_timeout (xosd *osd, int timeout) { | |
osd->timeout = timeout; | |
osd->timeout_time = time (NULL) + timeout; | |
return 0; | |
} | |
//static void xosd_update_pos (xosd *osd) { | |
// osd->x = 0; | |
// //if (osd->pos == XOSD_bottom) | |
// // osd->y = XDisplayHeight (osd->display, osd->screen) - osd->height - osd->offset; | |
// //else | |
// osd->y = osd->offset; | |
// | |
// XMoveWindow (osd->display, osd->window, osd->x, osd->y); | |
//} | |
// | |
// | |
//int xosd_set_offset (xosd *osd, int offset) { | |
// assert (osd); | |
// osd->offset = offset; | |
// xosd_update_pos (osd); | |
// return 0; | |
//} | |
xosd *xosd_init (char *colour, int timeout, int offset, | |
int shadow_offset) { | |
xosd *osd; | |
int event_basep, error_basep, inputmask, i; | |
char *display; | |
XSetWindowAttributes setwinattr; | |
long data; | |
char **missing; | |
int nmissing; | |
char *defstr; | |
Atom a; | |
/* fprintf(stderr, "Hello!\n"); */ | |
display = getenv ("DISPLAY"); | |
if (!display) | |
{ | |
perror ("No display\n"); | |
return NULL; | |
} | |
osd = malloc (sizeof (xosd)); | |
sem_init (&osd->mutex, 0, 1); | |
osd->buttonstate = 0; | |
osd->is_exposing = 0; | |
osd->display = XOpenDisplay (display); | |
osd->screen = XDefaultScreen (osd->display); | |
if (!osd->display) | |
{ | |
perror ("Cannot open display\n"); | |
free(osd); | |
return NULL; | |
} | |
if (!XShapeQueryExtension (osd->display, &event_basep, &error_basep)) | |
{ | |
fprintf (stderr, "X-Server does not support shape extension\n"); | |
free(osd); | |
return NULL; | |
} | |
XVisualInfo vinfo; | |
XMatchVisualInfo(osd->display, DefaultScreen(osd->display), 32, TrueColor, &vinfo); | |
osd->visual = vinfo.visual; //DefaultVisual (osd->display, osd->screen); | |
osd->depth = vinfo.depth; //DefaultDepth (osd->display, osd->screen); | |
//~ osd->visual = DefaultVisual (osd->display, osd->screen); | |
//~ osd->depth = DefaultDepth (osd->display, osd->screen); | |
// with DefaultVisual and DefaultDepth: visual 588181040, depth 24; then no alpha draws and mouse cursor xputimage, but has Gnome3 window decoration outline; can have just override redirect | |
// with DefaultVisual and forced: osd->depth = 32; visual 1736310320, depth 32: badmatch for createwindow, no matter what | |
// with vinfo.visual and depth: must have at least CWColormap|CWBorderPixel (cannot use CWBorderPixmap instead) in addition to CWOverrideRedirect, before it passes - but then, no Gnome3 window decoration outline | |
//~ printf("visual %d, depth %d\n", osd->visual, osd->depth); | |
osd->width = WINDOW_WIDTH; //XDisplayWidth (osd->display, osd->screen)-100; | |
osd->height = WINDOW_HEIGHT; //100; //extents->max_logical_extent.height * NLINES + 10; | |
// testprogram.c | |
setwinattr.colormap = XCreateColormap( osd->display, DefaultRootWindow(osd->display), osd->visual, AllocNone) ; | |
//setwinattr.event_mask = ExposureMask | vinfo; | |
setwinattr.override_redirect = 1; | |
//~ setwinattr.background_pixmap = CopyFromParent; //None ; | |
setwinattr.border_pixel = CopyFromParent; //0xFFFFFFFF ; | |
//~ setwinattr.border_pixmap = CopyFromParent ; | |
osd->window = XCreateWindow (osd->display, | |
XRootWindow (osd->display, osd->screen), | |
0, 0, | |
osd->width, osd->height, | |
0, | |
osd->depth, | |
InputOutput, //CopyFromParent, | |
osd->visual, | |
//~ CWColormap|CWBorderPixel|CWBackPixmap|CWOverrideRedirect, // CWEventMask| // fails if CWBorderPixel not present | |
CWColormap|CWBorderPixel|CWOverrideRedirect, // | |
&setwinattr); | |
XStoreName (osd->display, osd->window, "XOSD"); | |
//xosd_set_offset (osd, offset); | |
// note: as soon as this glcontext was added/made current - the XPutImage started being transparent! | |
// actually - as if not needed? | |
//~ osd->glcontext = glXCreateContext( osd->display, &vinfo, 0, True ) ; | |
//~ if (!osd->glcontext) | |
//~ { | |
//~ printf("X11 server '%s' does not support OpenGL\n", getenv( "DISPLAY" ) ) ; | |
//~ exit(1) ; | |
//~ } | |
//~ glXMakeCurrent( osd->display, osd->window, osd->glcontext ) ; | |
// XShapeCombineRectangles: do not pick up any mouse events on this osd window | |
XShapeCombineRectangles (osd->display, | |
osd->window, | |
ShapeInput, | |
0, //hints.x, | |
0, //hints.y, | |
NULL, 0, | |
ShapeSet, | |
YXBanded); | |
osd->bitmap = XCreatePixmap (osd->display, osd->window, | |
osd->width, osd->height, | |
1); | |
osd->gc = XCreateGC (osd->display, osd->window, 0, NULL); | |
osd->bitmap_gc = XCreateGC (osd->display, osd->bitmap, 0, NULL); | |
osd->bitmap_gc_back = XCreateGC (osd->display, osd->bitmap, | |
0, NULL); | |
osd->transp_gc = XCreateGC (osd->display, osd->window, 0, NULL); | |
XSetForeground (osd->display, osd->bitmap_gc_back, | |
BlackPixel (osd->display, osd->screen)); | |
XSetBackground (osd->display, osd->bitmap_gc_back, | |
WhitePixel (osd->display, osd->screen)); | |
XSetForeground (osd->display, osd->bitmap_gc, | |
WhitePixel (osd->display, osd->screen)); | |
XSetBackground (osd->display, osd->bitmap_gc, | |
BlackPixel (osd->display, osd->screen)); | |
XSetForeground (osd->display, osd->transp_gc, 0x00000000); //0x401002010); // 0xAARRGGBB, hex | |
set_colour (osd, colour); | |
set_timeout (osd, timeout); | |
inputmask = ExposureMask ; | |
XSelectInput (osd->display, osd->window, inputmask); | |
XGetWindowAttributes(osd->display, osd->window, &osd->orig_attr); | |
//~ XChangeWindowAttributes(osd->display, osd->window, valuemask, orig_attr); | |
//~ // https://stackoverflow.com/questions/13395179/empty-or-transparent-window-with-xlib-showing-border-lines-only | |
//XVisualInfo vinfo; | |
//XMatchVisualInfo(osd->display, DefaultScreen(osd->display), 32, TrueColor, &vinfo); | |
//osd->transp_attr.colormap = XCreateColormap(osd->display, osd->window, vinfo.visual, AllocNone); | |
//osd->transp_attr.border_pixel = 0; | |
//osd->transp_attr.background_pixel = 0; | |
data = 6; | |
a = XInternAtom (osd->display, "_WIN_LAYER", True); | |
if (a != None) | |
{ | |
XChangeProperty (osd->display, | |
osd->window, | |
XInternAtom (osd->display, "_WIN_LAYER", True), | |
XA_CARDINAL, | |
32, | |
PropModeReplace, | |
(unsigned char *)&data, | |
1); | |
} | |
osd->mapped = 0; | |
osd->done = 0; | |
osd->shadow_offset = shadow_offset; | |
#ifdef TRY_CAIRO | |
//~ int cairowidth, cairoheight; cairowidth = cairoheight = 30; // limits the drawing surface | |
int cairowidth = osd->width, cairoheight = osd->height; | |
cairo_surface_t *surf = cairo_xlib_surface_create(osd->display, osd->window, | |
DefaultVisual(osd->display, osd->screen), | |
cairowidth, cairoheight); | |
osd->cr = cairo_create(surf); | |
#endif | |
pthread_create (&osd->event_thread, NULL, event_loop, osd); | |
pthread_create (&osd->timeout_thread, NULL, timeout_loop, osd); | |
return osd; | |
} | |
int xosd_uninit (xosd *osd) { | |
int i; | |
assert (osd); | |
MUTEX_GET (); | |
osd->done = 1; | |
MUTEX_RELEASE (); | |
pthread_join (osd->event_thread, NULL); | |
pthread_join (osd->timeout_thread, NULL); | |
#ifdef TRY_CAIRO | |
cairo_surface_t *surf = cairo_get_target (osd->cr); | |
cairo_destroy(osd->cr); | |
cairo_surface_destroy(surf); | |
#endif | |
XFreePixmap (osd->display, osd->bitmap); | |
XDestroyWindow (osd->display, osd->window); | |
sem_destroy (&osd->mutex); | |
free (osd); | |
return 0; | |
} | |
/* Return 1 if XI2 is available, 0 otherwise */ | |
static int has_xi2(Display *dpy) | |
{ | |
int major, minor; | |
int rc; | |
/* We support XI 2.2 */ | |
major = 2; | |
minor = 2; | |
rc = XIQueryVersion(dpy, &major, &minor); | |
if (rc == BadRequest) { | |
printf("No XI2 support. Server supports version %d.%d only.\n", major, minor); | |
return 0; | |
} else if (rc != Success) { | |
fprintf(stderr, "Internal Error! This is a bug in Xlib.\n"); | |
} | |
printf("XI2 supported. Server provides version %d.%d.\n", major, minor); | |
return 1; | |
} | |
static void select_events(Display *dpy, Window win) | |
{ | |
XIEventMask evmasks[1]; | |
unsigned char mask1[(XI_LASTEVENT + 7)/8]; | |
memset(mask1, 0, sizeof(mask1)); | |
/* select for button and key events from all master devices */ | |
XISetMask(mask1, XI_RawMotion); | |
XISetMask(mask1, XI_RawButtonPress); | |
XISetMask(mask1, XI_RawButtonRelease); | |
evmasks[0].deviceid = XIAllMasterDevices; | |
evmasks[0].mask_len = sizeof(mask1); | |
evmasks[0].mask = mask1; | |
XISelectEvents(dpy, win, evmasks, 1); | |
// note: Expose is when a window is newly redrawn (e.g. alt-tab switching) | |
// via https://mail.gnome.org/archives/commits-list/2012-November/msg05208.html | |
unsigned long event_mask = ExposureMask | PropertyChangeMask; | |
XSelectInput (dpy, win, event_mask); | |
XFlush(dpy); | |
} | |
int main (int argc, char **argv) | |
{ | |
Display *dpy; | |
int xi_opcode, event, xfixesevent, error; | |
XEvent ev; | |
dpy = XOpenDisplay(NULL); | |
if (!dpy) { | |
fprintf(stderr, "Failed to open display.\n"); | |
return -1; | |
} | |
if (!XQueryExtension(dpy, "XInputExtension", &xi_opcode, &event, &error)) { | |
printf("X Input extension not available.\n"); | |
return -1; | |
} | |
if (!XFixesQueryExtension(dpy, &xfixesevent, &error)) { | |
printf("XFixes not available!\n"); | |
return -1; | |
} | |
if (!has_xi2(dpy)) | |
return -1; | |
/* select for XI2 events */ | |
select_events(dpy, DefaultRootWindow(dpy)); | |
/* Register for cursor events (https://github.com/dnschneid/crouton/blob/master/src/fbserver.c) */ | |
XFixesSelectCursorInput(dpy, DefaultRootWindow(dpy), XFixesDisplayCursorNotifyMask); // event 88?! | |
struct xosd* osd = xosd_init ("LawnGreen", 3, 20, 1); | |
xosd_show(osd); | |
osd->fixesEvent = xfixesevent; | |
while(1) { | |
XGenericEventCookie *cookie = &ev.xcookie; | |
XIRawEvent *re; | |
Window root_ret, child_ret; | |
int root_x, root_y; | |
int win_x, win_y; | |
unsigned int mask; | |
XNextEvent(dpy, &ev); | |
#ifdef DEBUGPRINT | |
printf("next: %d\n", ev.type); | |
#endif | |
int doForceRedraw = 0; | |
// while (XCheckTypedEvent(dpy, xfixesevent + XFixesCursorNotify, &ev)) { | |
if (ev.type == osd->fixesEvent + XFixesCursorNotify) { | |
MUTEX_GET (); | |
XFixesCursorNotifyEvent* curev = (XFixesCursorNotifyEvent*)&ev; | |
char* name = XGetAtomName(dpy, curev->cursor_name); | |
strncpy(osd->cursor_name, name, 255); osd->cursor_name[255] = '\0'; | |
osd->cursor_serial = curev->cursor_serial; | |
osd->cursor_image = XFixesGetCursorImage(dpy); // XFixesCursorImage *cur | |
#ifdef DEBUGPRINT | |
printf(" cursor! %ld %s (x,y %d %d x,y(hot) %d %d w,h %d %d)\n", osd->cursor_serial, osd->cursor_name, osd->cursor_image->x, osd->cursor_image->y, osd->cursor_image->xhot, osd->cursor_image->yhot, osd->cursor_image->width, osd->cursor_image->height); | |
#endif | |
XFree(name); | |
if (osd->cursor_ximage) { XDestroyImage(osd->cursor_ximage); } | |
if (osd->cursor_ximageB) { XDestroyImage(osd->cursor_ximageB); } | |
int count_return; | |
//~ osd->cursor_ximage = XCreateImage(osd->display, osd->visual, osd->depth, ZPixmap, 0, ); // too many manual args | |
osd->cursor_ximage = XGetImage(osd->display, osd->window, 0, 0, osd->cursor_image->width, osd->cursor_image->height, AllPlanes, XYPixmap); | |
osd->cursor_ximageB = XGetImage(osd->display, osd->window, 0, 0, osd->cursor_image->width, osd->cursor_image->height, AllPlanes, XYPixmap); | |
// copy mouse cursor pixels; https://stackoverflow.com/questions/22891351/structure-of-xfixescursorimage | |
unsigned char r,g,b,a; | |
unsigned short row,col,pos; | |
for(pos = row = 0;row<osd->cursor_image->height; row++) | |
{ | |
for(col=0;col < osd->cursor_image->width;col++,pos++) | |
{ | |
a = (unsigned char)((osd->cursor_image->pixels[pos] >> 24) & 0xff); | |
r = (unsigned char)((osd->cursor_image->pixels[pos] >> 16) & 0xff); | |
g = (unsigned char)((osd->cursor_image->pixels[pos] >> 8) & 0xff); | |
b = (unsigned char)((osd->cursor_image->pixels[pos] >> 0) & 0xff); | |
// https://github.com/Zygo/xscreensaver/blob/master/hacks/ximage-loader.c | |
unsigned long rgba_pixel = 0; | |
rgba_pixel = ((r << 24) | (g << 16) | (b << 8) | a); | |
//put_pixel_in_ximage(img->x+col,img->y+row, convert_to_ximage_pixel(r,g,b,a)); | |
XPutPixel(osd->cursor_ximageB, col, row, rgba_pixel); // not quite correct colors! | |
XPutPixel(osd->cursor_ximage, col, row, osd->cursor_image->pixels[pos]); // better colors with direct, without conversion | |
} | |
} | |
osd->cursor_updated = 1; | |
doForceRedraw = 1; | |
MUTEX_RELEASE (); | |
} | |
if (cookie->type != GenericEvent || | |
cookie->extension != xi_opcode || | |
!XGetEventData(dpy, cookie)) | |
continue; | |
switch (cookie->evtype) { | |
// note: left click is re->detail==1; right click is re->detail==3; so buttonstate is 0, 1, 4, 5 | |
case XI_RawButtonPress: | |
re = (XIRawEvent *) cookie->data; | |
unsigned char btnpressed = (unsigned char)re->detail; | |
osd->buttonstate |= (unsigned char)(1<<(btnpressed-1)); | |
#ifdef DEBUGPRINT | |
printf("XI_RawButtonPress %d %d\n", re->detail, osd->buttonstate); | |
#endif | |
break; | |
case XI_RawButtonRelease: | |
re = (XIRawEvent *) cookie->data; | |
osd->buttonstate &= ~((unsigned char)(1<<(btnpressed-1))); | |
#ifdef DEBUGPRINT | |
printf("XI_RawButtonRelease %d %d\n", re->detail, osd->buttonstate); | |
#endif | |
break; | |
case XI_RawMotion: | |
re = (XIRawEvent *) cookie->data; | |
XQueryPointer(dpy, DefaultRootWindow(dpy), | |
&root_ret, &child_ret, &root_x, &root_y, &win_x, &win_y, &mask); | |
int enlargefactor = 1; | |
#ifdef MOUSE_TRAIL | |
osd->mousehistpnts[0].x = osd->mousehistpnts[1].x; | |
osd->mousehistpnts[0].y = osd->mousehistpnts[1].y; | |
//~ osd->mousehistpnts[1].x = enlargefactor*(root_x - osd->mouse_root_x); | |
//~ osd->mousehistpnts[1].y = enlargefactor*(root_y - osd->mouse_root_y); | |
// raw_values are mouse pos deltas | |
osd->mousehistpnts[1].x = enlargefactor*(re->raw_values[0]); | |
osd->mousehistpnts[1].y = enlargefactor*(re->raw_values[1]); | |
#endif | |
osd->mouse_root_x = root_x; | |
osd->mouse_root_y = root_y; | |
#ifdef DEBUGPRINT | |
printf ("raw %g,%g root %d,%d root_ret 0x%08lX child_ret 0x%08lX \n", | |
re->raw_values[0], re->raw_values[1], | |
osd->mouse_root_x, osd->mouse_root_y, root_ret, child_ret); | |
#endif | |
//~ if ( osd->is_exposing == 0 ) { | |
//~ osd->is_exposing = 1; | |
//~ force_redraw(osd); | |
//~ } else osd->is_exposing = 0; | |
doForceRedraw = 1; | |
break; | |
} | |
XFreeEventData(dpy, cookie); | |
if (doForceRedraw == 1) { | |
doForceRedraw = 0; | |
force_redraw(osd); | |
} | |
} | |
xosd_uninit(osd); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment