Skip to content

Instantly share code, notes, and snippets.

@sdaau
Last active July 20, 2019 18:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sdaau/867804ad4b7d05cd926179f03285d40e to your computer and use it in GitHub Desktop.
Save sdaau/867804ad4b7d05cd926179f03285d40e to your computer and use it in GitHub Desktop.
xosd_track_cursor.c
/* 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