Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

X11: Draw overlay / Remove drawings from overlay

Tags:

x11

gtk

cairo

xlib

I need to draw a shape directly on screen (a little arrow) in X11, it serves as an overlay. I am searching for about an hour no with no good results. Could anyone provide me a good entry point for what I need? The technologies I can use are cairo, gtk or XLib.

Everything I have found so far either depends on Composition, which is not always available, or will create a white shape behind my arrow (rectangle, a window).

EDIT: I am now able to draw an X11 Overlay using Composite and Cairo. I do it this way (Note: This is a minimal example. It has few to no error checking!!!). Start from Terminal to be able to quit it!

// COMPILE WITH: g++ -o overlay overlay.cc -lXfixes -lXcomposite -lX11 `pkg-config --cflags --libs cairo`
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/Xcomposite.h>
#include <X11/extensions/Xfixes.h>
#include <X11/extensions/shape.h>
#include <cairo/cairo.h>
#include <cairo/cairo-xlib.h>

#include <stdio.h>
#include <unistd.h>

Display *display_;
int old_x_, old_y_;
cairo_surface_t *surf_;
Window overlay_;
int screen_;
int height_;
int width_;
cairo_t *cr_;


void paint_cursor(int new_x, int new_y, bool first = false)
{

    if (!first)
    {
        cairo_set_operator(cr_, CAIRO_OPERATOR_CLEAR);
        cairo_rectangle(cr_, old_x_, old_y_, 20, 20);
        cairo_fill(cr_);
    }
    old_x_ = new_x;
    old_y_ = new_y;

    cairo_set_operator(cr_, CAIRO_OPERATOR_SOURCE);
    cairo_move_to(cr_, new_x, new_y);
    cairo_line_to(cr_, new_x + 0, new_y + 16);
    cairo_line_to(cr_, new_x + 4, new_y + 13);
    cairo_line_to(cr_, new_x + 7, new_y + 18);
    cairo_line_to(cr_, new_x + 9, new_y + 17);
    cairo_line_to(cr_, new_x + 6, new_y + 12);
    cairo_line_to(cr_, new_x + 11, new_y + 12);
    cairo_line_to(cr_, new_x + 0, new_y + 0);

    cairo_set_source_rgba(cr_, 0.0, 0.0, 0.0, 0.5);
    cairo_stroke_preserve(cr_);
    cairo_set_source_rgba(cr_, 0.0, 1.0, 0.0, 0.5);
    cairo_fill(cr_);
}

int main()
{

    display_ = ::XOpenDisplay(0);
    if (!display_)
    {
        return -1;
    }

    screen_ = ::XDefaultScreen(display_);
    Window root = RootWindow(display_, screen_);

    ::XCompositeRedirectSubwindows(display_, root, CompositeRedirectAutomatic);
    ::XSelectInput(display_, root, SubstructureNotifyMask);

    overlay_ = ::XCompositeGetOverlayWindow(display_, root);

    XserverRegion region = ::XFixesCreateRegion(display_, 0, 0);
    ::XFixesSetWindowShapeRegion(display_, overlay_, ShapeBounding, 0, 0, 0);
    ::XFixesSetWindowShapeRegion(display_, overlay_, ShapeInput, 0, 0, region);
    ::XFixesDestroyRegion(display_, region);

    width_ = DisplayWidth(display_, screen_);
    height_ = DisplayHeight(display_, screen_);

    surf_ = ::cairo_xlib_surface_create(display_, overlay_, DefaultVisual(display_, screen_), width_, height_);

    cr_ = ::cairo_create(surf_);
    ::XSelectInput(display_, overlay_, ExposureMask);

    old_x_ = 0;
    old_y_ = 0;
    paint_cursor(0, 0, true);

    XEvent ev;

    Window root_return, child_return;
    int root_x_return, root_y_return;
    int win_x_return, win_y_return;
    unsigned int mask;


    for (;;)
    {
        XQueryPointer(display_, root, &root_return, &child_return, &root_x_return, &root_y_return, &win_x_return, &win_y_return, &mask);
        paint_cursor(root_x_return, root_y_return);
        printf("Paint\n");
    }

    return 0;
}

The question that is left: How to I remove the old cursor that was drawn? I tried XClearArea, I tried to overpaint with Cairo, I tried the Cairo Clear operator, etc. Nothing worked. Can someone point me here in the right direction?

like image 431
Nidhoegger Avatar asked Aug 17 '16 12:08

Nidhoegger


1 Answers

This answer should help you out:

"How can I draw selection rectangle on screen for my script?"

Credits go to sdbbs at askubuntu.com

#include<stdio.h>
#include<stdlib.h>
#include<X11/Xlib.h>
#include<X11/cursorfont.h>
#include<unistd.h> // added for sleep/usleep

// original from [https://bbs.archlinux.org/viewtopic.php?id=85378 Select a screen area with mouse and return the geometry of this area? / Programming & Scripting / Arch Linux Forums]
// build with (Ubuntu 14.04):
// gcc -Wall xrectsel.c -o xrectsel -lX11

int main(void)
{
  int rx = 0, ry = 0, rw = 0, rh = 0;
  int rect_x = 0, rect_y = 0, rect_w = 0, rect_h = 0;
  int btn_pressed = 0, done = 0;

  XEvent ev;
  Display *disp = XOpenDisplay(NULL);

  if(!disp)
    return EXIT_FAILURE;

  Screen *scr = NULL;
  scr = ScreenOfDisplay(disp, DefaultScreen(disp));

  Window root = 0;
  root = RootWindow(disp, XScreenNumberOfScreen(scr));

  Cursor cursor, cursor2;
  cursor = XCreateFontCursor(disp, XC_left_ptr);
  cursor2 = XCreateFontCursor(disp, XC_lr_angle);

  XGCValues gcval;
  gcval.foreground = XWhitePixel(disp, 0);
  gcval.function = GXxor;
  gcval.background = XBlackPixel(disp, 0);
  gcval.plane_mask = gcval.background ^ gcval.foreground;
  gcval.subwindow_mode = IncludeInferiors;

  GC gc;
  gc = XCreateGC(disp, root,
                 GCFunction | GCForeground | GCBackground | GCSubwindowMode,
                 &gcval);

  /* this XGrab* stuff makes XPending true ? */
  if ((XGrabPointer
       (disp, root, False,
        ButtonMotionMask | ButtonPressMask | ButtonReleaseMask, GrabModeAsync,
        GrabModeAsync, root, cursor, CurrentTime) != GrabSuccess))
    printf("couldn't grab pointer:");

  if ((XGrabKeyboard
       (disp, root, False, GrabModeAsync, GrabModeAsync,
        CurrentTime) != GrabSuccess))
    printf("couldn't grab keyboard:");

  // see also: http://stackoverflow.com/questions/19659486/xpending-cycle-is-making-cpu-100
  while (!done) {
    //~ while (!done && XPending(disp)) {
      //~ XNextEvent(disp, &ev);
    if (!XPending(disp)) { usleep(1000); continue; } // fixes the 100% CPU hog issue in original code
    if ( (XNextEvent(disp, &ev) >= 0) ) {
      switch (ev.type) {
        case MotionNotify:
        /* this case is purely for drawing rect on screen */
          if (btn_pressed) {
            if (rect_w) {
              /* re-draw the last rect to clear it */
              XDrawRectangle(disp, root, gc, rect_x, rect_y, rect_w, rect_h);
            } else {
              /* Change the cursor to show we're selecting a region */
              XChangeActivePointerGrab(disp,
                                       ButtonMotionMask | ButtonReleaseMask,
                                       cursor2, CurrentTime);
            }
            rect_x = rx;
            rect_y = ry;
            rect_w = ev.xmotion.x - rect_x;
            rect_h = ev.xmotion.y - rect_y;

            if (rect_w < 0) {
              rect_x += rect_w;
              rect_w = 0 - rect_w;
            }
            if (rect_h < 0) {
              rect_y += rect_h;
              rect_h = 0 - rect_h;
            }
            /* draw rectangle */
            XDrawRectangle(disp, root, gc, rect_x, rect_y, rect_w, rect_h);
            XFlush(disp);
          }
          break;
        case ButtonPress:
          btn_pressed = 1;
          rx = ev.xbutton.x;
          ry = ev.xbutton.y;
          break;
        case ButtonRelease:
          done = 1;
          break;
      }
    }
  }
  /* clear the drawn rectangle */
  if (rect_w) {
    XDrawRectangle(disp, root, gc, rect_x, rect_y, rect_w, rect_h);
    XFlush(disp);
  }
  rw = ev.xbutton.x - rx;
  rh = ev.xbutton.y - ry;
  /* cursor moves backwards */
  if (rw < 0) {
    rx += rw;
    rw = 0 - rw;
  }
  if (rh < 0) {
    ry += rh;
    rh = 0 - rh;
  }

  XCloseDisplay(disp);

  printf("%dx%d+%d+%d\n",rw,rh,rx,ry);

  return EXIT_SUCCESS;
}
like image 125
Gerald Avatar answered Oct 01 '22 00:10

Gerald