2016-08-17 3 views
5

J'ai besoin de dessiner une forme directement sur l'écran (une petite flèche) dans X11, elle sert de superposition. Je cherche environ une heure non sans de bons résultats. Quelqu'un pourrait-il me fournir un bon point d'entrée pour ce dont j'ai besoin? Les technologies que je peux utiliser sont cairo, gtk ou XLib. Tout ce que j'ai trouvé jusqu'ici dépend de la composition, qui n'est pas toujours disponible, ou crée une forme blanche derrière ma flèche (rectangle, une fenêtre).X11: Dessiner une superposition/Supprimer des dessins d'une superposition

EDIT: Je suis maintenant capable de dessiner une superposition X11 en utilisant Composite et Cairo. Je le fais de cette façon (Note: Ceci est un exemple minimal, il y a peu ou pas de vérification d'erreur !!!). Commencez à partir du terminal pour pouvoir le quitter!

// 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; 
} 

La question qui reste: Comment supprimer l'ancien curseur qui a été dessiné? J'ai essayé XClearArea, j'ai essayé de peindre avec Cairo, j'ai essayé l'opérateur Cairo Clear, etc. Rien n'a fonctionné. Quelqu'un peut-il me pointer ici dans la bonne direction?

+2

Core X11 ne fait pas cela, vous aurez besoin d'une sorte d'extension. Vous pouvez utiliser l'extension Composite ou Shape. La forme est plus facilement disponible que Composite. Vous pouvez également utiliser un visuel de superposition si disponible (nécessite une prise en charge OpenGL et overlay dans le matériel). –

+0

Vous pouvez également dessiner directement sur des fenêtres existantes, mais cela n'est pas recommandé. Si un autre programme essaie de repeindre en même temps, le résultat peut ne pas s'afficher correctement. –

+0

Regardez la source de xeyes. Ancien mais fonctionne. –

Répondre

0

J'ai résolu ce problème moi-même en utilisant maintenant Shape extension de X11 et une boucle de message séparé dans un thread. Ce n'est pas idéal mais ça marche. Mauvais code postal dès que j'ai de nouveau accès au code (malade atm).

0

Cette réponse devrait vous aider:

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

Crédits vont sdbbs à 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; 
} 
+0

Ceci dessine un rectangle par XORing l'écran avec le rectangle. Bien que cette méthode fonctionne correctement sur un écran statique, elle échoue avec les fenêtres qui sont mises à jour en permanence. –

+0

Je suis désolé, mais ce n'est pas ce que je veux. Cela remplace mon curseur. Je veux en avoir un second. – Nidhoegger

+1

@Nidhoegger Vous n'avez pas besoin de remplacer le curseur. Changer 'cursor' et' cursor2' à 'None' dans les appels pour saisir des fonctions. –