2016-08-17 174 views
5

我需要在X11中直接在屏幕上繪製一個形狀(一個小箭頭),它用作覆蓋圖。我正在尋找大約一個小時沒有沒有好的結果。任何人都可以爲我提供一個很好的切入點嗎?我可以使用的技術是cairo,gtk或XLib。X11:繪製覆蓋圖/從覆蓋圖中移除繪圖

到目前爲止我發現的所有東西要麼取決於構圖,這並不總是可用,要麼會在我的箭頭(矩形,窗口)後面創建一個白色的形狀。

編輯:我現在能夠繪製使用複合和開羅X11覆蓋。我這樣做(注意:這是一個簡單的例子,它幾乎沒有錯誤檢查!!!)。從終端開始,以便能夠退出它!

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

剩下的問題:如何刪除繪製的舊遊標?我嘗試過XClearArea,我試圖覆蓋開羅,我試過開羅清除運營商,等等。沒有任何工作。有人能在正確的方向指向我嗎?

+2

核心X11不這樣做,你將需要某種類型的擴展。您可以使用複合或形狀擴展。形狀比複合材料更容易獲得。如果可用,您也可以使用疊加視覺(需要硬件中的OpenGL和疊加支持)。 –

+0

您也可以直接在現有窗口上繪圖,但不建議這樣做。如果其他程序試圖同時重繪,結果可能看起來不正確。 –

+0

看看xeyes的來源。古代但有用。 –

回答

0

我已經解決了這個問題,我自己現在使用來自X11的形狀擴展和線程中的單獨消息循環。它不理想,但它的工作原理。只要我再次訪問該代碼,病態代碼(病態atm)。

0

這個答案應該幫助你:

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

現金去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

這會通過將矩形與屏幕異或來繪製矩形。雖然此方法在靜態屏幕上可以正常工作,但它會因不斷更新的窗口而失敗。 –

+0

我很抱歉,但這不是我想要的。這取代了我的光標。我想要第二個。 – Nidhoegger

+1

@Nidhoegger您不必更換光標。在調用抓取函數時將'cursor'和'cursor2'改爲'None'。 –