Create custom cursors via XRender.
authorLaszlo Agocs <laszlo.p.agocs@nokia.com>
Wed, 8 Jun 2011 15:41:12 +0000 (17:41 +0200)
committerLaszlo Agocs <laszlo.p.agocs@nokia.com>
Thu, 9 Jun 2011 09:00:02 +0000 (11:00 +0200)
Reviewed-by: Samuel Rødal
src/plugins/platforms/xcb/qxcbconnection.cpp
src/plugins/platforms/xcb/qxcbconnection.h
src/plugins/platforms/xcb/qxcbcursor.cpp
src/plugins/platforms/xcb/qxcbimage.cpp
src/plugins/platforms/xcb/qxcbimage.h
src/plugins/platforms/xcb/xcb.pro

index 1f966b1..0af71fa 100644 (file)
 #include <X11/Xlib-xcb.h>
 #endif
 
+#ifdef XCB_USE_RENDER
+#include <xcb/render.h>
+#endif
+
 #ifdef XCB_USE_EGL //dont pull in eglext prototypes
 #include <EGL/egl.h>
 #endif
@@ -126,6 +130,7 @@ QXcbConnection::QXcbConnection(const char *displayName)
     m_drag = new QXcbDrag(this);
 
     initializeXFixes();
+    initializeXRender();
 
 #ifdef XCB_USE_DRI2
     initializeDri2();
@@ -848,6 +853,23 @@ void QXcbConnection::initializeXFixes()
     free(xfixes_query);
 }
 
+void QXcbConnection::initializeXRender()
+{
+#ifdef XCB_USE_RENDER
+    xcb_generic_error_t *error = 0;
+    xcb_render_query_version_cookie_t xrender_query_cookie = xcb_render_query_version(m_connection,
+                                                                                      XCB_RENDER_MAJOR_VERSION,
+                                                                                      XCB_RENDER_MINOR_VERSION);
+    xcb_render_query_version_reply_t *xrender_query = xcb_render_query_version_reply(m_connection,
+                                                                                     xrender_query_cookie, &error);
+    if (!xrender_query || error || (xrender_query->major_version == 0 && xrender_query->minor_version < 5)) {
+        qWarning("Failed to initialize XRender");
+        free(error);
+    }
+    free(xrender_query);
+#endif
+}
+
 #if defined(XCB_USE_EGL)
 bool QXcbConnection::hasEgl() const
 {
index b148b56..9413b68 100644 (file)
@@ -293,6 +293,7 @@ private:
     void initializeAllAtoms();
     void sendConnectionEvent(QXcbAtom::Atom atom, uint id = 0);
     void initializeXFixes();
+    void initializeXRender();
 #ifdef XCB_USE_DRI2
     void initializeDri2();
 #endif
index cf1e527..f6856d5 100644 (file)
@@ -489,22 +489,18 @@ xcb_cursor_t QXcbCursor::createBitmapCursor(QCursor *cursor)
 {
     xcb_connection_t *conn = xcb_connection();
     QPoint spot = cursor->hotSpot();
-    xcb_pixmap_t cp;
-    xcb_pixmap_t mp;
-    if (cursor->pixmap().depth() > 1) {
-        // ### this will result in monochrome cursors
-        cp = qt_xcb_XPixmapFromBitmap(m_screen, cursor->bitmap()->toImage());
-        mp = qt_xcb_XPixmapFromBitmap(m_screen, cursor->mask()->toImage());
-    } else {
-        cp = qt_xcb_XPixmapFromBitmap(m_screen, cursor->bitmap()->toImage());
-        mp = qt_xcb_XPixmapFromBitmap(m_screen, cursor->mask()->toImage());
-    }
-    xcb_cursor_t c = xcb_generate_id(conn);
-    xcb_create_cursor(conn, c, cp, mp, 0, 0, 0, 0xFFFF, 0xFFFF, 0xFFFF,
-                      spot.x(), spot.y());
-    xcb_free_pixmap(conn, cp);
-    if (mp)
+    xcb_cursor_t c = XCB_NONE;
+    if (cursor->pixmap().depth() > 1)
+        c = qt_xcb_createCursorXRender(m_screen, cursor->pixmap().toImage(), spot);
+    if (!c) {
+        xcb_pixmap_t cp = qt_xcb_XPixmapFromBitmap(m_screen, cursor->bitmap()->toImage());
+        xcb_pixmap_t mp = qt_xcb_XPixmapFromBitmap(m_screen, cursor->mask()->toImage());
+        c = xcb_generate_id(conn);
+        xcb_create_cursor(conn, c, cp, mp, 0, 0, 0, 0xFFFF, 0xFFFF, 0xFFFF,
+                          spot.x(), spot.y());
+        xcb_free_pixmap(conn, cp);
         xcb_free_pixmap(conn, mp);
+    }
     return c;
 }
 
index 6c8c4e3..3fb5e2c 100644 (file)
 #include "qxcbimage.h"
 #include <QtGui/QColor>
 #include <QtGui/private/qimage_p.h>
+#include <QtGui/private/qdrawhelper_p.h>
+#ifdef XCB_USE_RENDER
+#include <xcb/render.h>
+// 'template' is used as a function argument name in xcb_renderutil.h
+#define template template_param
+// extern "C" is missing too
+extern "C" {
+#include <xcb/xcb_renderutil.h>
+}
+#undef template
+#endif
 
 QT_BEGIN_NAMESPACE
 
@@ -177,4 +188,73 @@ xcb_pixmap_t qt_xcb_XPixmapFromBitmap(QXcbScreen *screen, const QImage &image)
     return pm;
 }
 
+xcb_cursor_t qt_xcb_createCursorXRender(QXcbScreen *screen, const QImage &image,
+                                        const QPoint &spot)
+{
+#ifdef XCB_USE_RENDER
+    xcb_connection_t *conn = screen->xcb_connection();
+    const int w = image.width();
+    const int h = image.height();
+    xcb_generic_error_t *error = 0;
+    xcb_render_query_pict_formats_cookie_t formatsCookie = xcb_render_query_pict_formats(conn);
+    xcb_render_query_pict_formats_reply_t *formatsReply = xcb_render_query_pict_formats_reply(conn,
+                                                                                              formatsCookie,
+                                                                                              &error);
+    if (!formatsReply || error) {
+        qWarning("createCursorXRender: query_pict_formats failed");
+        free(formatsReply);
+        free(error);
+        return XCB_NONE;
+    }
+    xcb_render_pictforminfo_t *fmt = xcb_render_util_find_standard_format(formatsReply,
+                                                                          XCB_PICT_STANDARD_ARGB_32);
+    if (!fmt) {
+        qWarning("createCursorXRender: Failed to find format PICT_STANDARD_ARGB_32");
+        free(formatsReply);
+        return XCB_NONE;
+    }
+
+    QImage img = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
+    xcb_image_t *xi = xcb_image_create(w, h, XCB_IMAGE_FORMAT_Z_PIXMAP,
+                                       32, 32, 32, 32,
+                                       QSysInfo::ByteOrder == QSysInfo::BigEndian ? XCB_IMAGE_ORDER_MSB_FIRST : XCB_IMAGE_ORDER_LSB_FIRST,
+                                       XCB_IMAGE_ORDER_MSB_FIRST,
+                                       0, 0, 0);
+    if (!xi) {
+        qWarning("createCursorXRender: xcb_image_create failed");
+        free(formatsReply);
+        return XCB_NONE;
+    }
+    xi->data = (uint8_t *) malloc(xi->stride * h);
+    memcpy(xi->data, img.constBits(), img.byteCount());
+
+    xcb_pixmap_t pix = xcb_generate_id(conn);
+    xcb_create_pixmap(conn, 32, pix, screen->root(), w, h);
+
+    xcb_render_picture_t pic = xcb_generate_id(conn);
+    xcb_render_create_picture(conn, pic, pix, fmt->id, 0, 0);
+
+    xcb_gcontext_t gc = xcb_generate_id(conn);
+    xcb_create_gc(conn, gc, pix, 0, 0);
+    xcb_image_put(conn, pix, gc, xi, 0, 0, 0);
+    xcb_free_gc(conn, gc);
+
+    xcb_cursor_t cursor = xcb_generate_id(conn);
+    xcb_render_create_cursor(conn, cursor, pic, spot.x(), spot.y());
+
+    free(xi->data);
+    xcb_image_destroy(xi);
+    xcb_render_free_picture(conn, pic);
+    xcb_free_pixmap(conn, pix);
+    free(formatsReply);
+    return cursor;
+
+#else
+    Q_UNUSED(screen);
+    Q_UNUSED(image);
+    Q_UNUSED(spot);
+    return XCB_NONE;
+#endif
+}
+
 QT_END_NAMESPACE
index 39a7abd..1e7f104 100644 (file)
@@ -56,6 +56,8 @@ QPixmap qt_xcb_pixmapFromXPixmap(QXcbConnection *connection, xcb_pixmap_t pixmap
                                  int width, int height, int depth,
                                  const xcb_visualtype_t *visual);
 xcb_pixmap_t qt_xcb_XPixmapFromBitmap(QXcbScreen *screen, const QImage &image);
+xcb_cursor_t qt_xcb_createCursorXRender(QXcbScreen *screen, const QImage &image,
+                                        const QPoint &spot);
 
 QT_END_NAMESPACE
 
index 0d9e969..c5ce6fa 100644 (file)
@@ -42,6 +42,9 @@ QT += gui-private core-private
 # needed by GLX, Xcursor, ...
 DEFINES += XCB_USE_XLIB
 
+# to support custom cursors with depth > 1
+DEFINES += XCB_USE_RENDER
+
 contains(QT_CONFIG, opengl) {
     QT += opengl
 
@@ -74,6 +77,7 @@ contains(QT_CONFIG, opengl) {
 
 LIBS += -lxcb -lxcb-image -lxcb-keysyms -lxcb-icccm -lxcb-sync -lxcb-xfixes
 contains(DEFINES, XCB_USE_XLIB): LIBS += -lX11 -lX11-xcb
+contains(DEFINES, XCB_USE_RENDER): LIBS += -lxcb-render -lxcb-render-util
 
 DEFINES += $$QMAKE_DEFINES_XCB
 LIBS += $$QMAKE_LIBS_XCB