0%

【Linux技术分享】X11学习(二)实现系统级别光标转圈

X11基础学习(二)实现系统级别光标转圈

本文学习X11设置系统的光标,使其全局状态为等待,参考开源项目linuxdeepin/startdde,中IO读写设置光标逻辑。代码已归纳至我的x11Cursor可以使用cmake直接编译使用

简介

  1. X11是什么

    X提供了GUI一个在unix系统下基本框架,可以绘图,移动窗口,显示窗口,与鼠标键盘一系列的交互。当前X已经更新到第11版所以简称为X11协议。与其类似的可以参考Wayland

局部设置鼠标为转圈状态

  1. setOverrideCursor

    在QT实现窗口上,进行全局鼠标光标加载利用QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));然后在使用QApplication::restoreOverrideCursor();对光标进行释放

    全局设置鼠标为转圈状态

  2. XcursorImagesLoadCursor

    在linux系统上观察,单一进程僵死或者无响应并不会导致光标转圈,当系统的CPU飙升时才会导致鼠标全局转圈。查看deepin的startdde中iowait代码发现存在时时监视CPU状态线程,当线程飙升时会调用XcursorImagesLoadCursor + XDefineCursor + XFixesChangeCursorByName函数设置光标,将光标样式设置为转圈样式就能实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    /**
    * @brief 改变当前光标样式
    * @param theme {const char *} 选择光标主题
    * @param src {const char *} 目标样式
    * @param dst {const char *} 待修改样式
    * @param dst {int} 光标大小
    */
    static void xc_change_cursor_by_name(Display *dpy, const char *theme,const char *src, const char* dst, int size)
    {
    if (size == -1) {
    size = XcursorGetDefaultSize(dpy);
    }

    // load cursor images
    XcursorImages *dst_images = xc_load_images(theme, dst, size);//①
    if (!dst_images) {
    XcursorImagesDestroy(dst_images);
    fprintf(stderr, "Failed to load cursor images: %s/%s\n", src, dst);
    return;
    }

    Cursor dst_cursor = XcursorImagesLoadCursor(dpy, dst_images);

    Window root = XDefaultRootWindow(dpy);
    XUndefineCursor(dpy, root);
    XDefineCursor(dpy, root, dst_cursor);
    printf("Will change cursor: %s --> %lu(%s)\n", src, dst_cursor, dst);
    XFixesChangeCursorByName(dpy, dst_cursor, src);//更改光标样式

    XcursorImagesDestroy(dst_images);
    XFreeCursor(dpy, dst_cursor);
    return ;
    }

    ①: Xcursor (mostly) follows the freedesktop.org spec for theming icons. The default search path it uses is ~/.local/share/icons, ~/.icons, /usr/share/icons, /usr/share/pixmaps. Within each of these directories, it searches for a directory using the theme name. Within the theme directory, it looks for cursor files in the ‘cursors’ subdirectory. It uses the first cursor file found along the path.
    If necessary, Xcursor also looks for a “index.theme” file in each theme directory to find inherited themes and searches along the path for those themes as well.

    If no theme is set, or if no cursor is found for the specified theme, Xcursor checks the “default” theme.

    上述为官方对them介绍,简而言之就是当你参数传入时他会从~/.local/share/icons, ~/.icons, /usr/share/icons, /usr/share/pixmaps.路径查找目标主题然后加载光标,如果主题不存在的设置为default

    隐藏光标

  3. XFixesHideCursor

    隐藏光标原理是将光标设置为x11 null cursor,设置光标方法和上面一样,当不需要隐藏时就设置x11 left_ptr cursor进行还原。如下为创建光标为NULL

    1
    2
    3
    4
    5
    6
    7
    8
    window = DefaultRootWindow(display);
    Cursor invisibleCursor;
    Pixmap bitmap;
    XColor black;
    static char data[] = { 0,0,0,0,0,0,0,0 };
    black.red = black.green = black.blue = 0;
    bitmap = XCreateBitmapFromData(display, window, data, 8, 8);
    invisibleCursor = XCreatePixmapCursor(display, bitmap, bitmap, &black, &black, 0, 0);

    如果不想用上述方法对光标进行设置,可以用libxfixes库进行光标隐藏和显示

    1
    2
    void XFixesHideCursor (Display *dpy, Window win);
    void XFixesShowCursor (Display *dpy, Window win);