From e99ab7de9bc73cada3424d657f48b63ea42147b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20R=C3=B8dal?= Date: Fri, 3 Jun 2011 13:39:12 +0200 Subject: Implemented QXcbIntegration::grabWindow(). --- src/plugins/platforms/xcb/qxcbconnection.cpp | 15 ++ src/plugins/platforms/xcb/qxcbconnection.h | 2 + src/plugins/platforms/xcb/qxcbintegration.cpp | 216 +++++++++++++++++++++++++- src/plugins/platforms/xcb/qxcbscreen.cpp | 25 +++ src/plugins/platforms/xcb/qxcbscreen.h | 3 + 5 files changed, 255 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index 5b0d9d8dbb..1655378037 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -740,6 +740,21 @@ QByteArray QXcbConnection::atomName(xcb_atom_t atom) return QByteArray(); } +const xcb_format_t *QXcbConnection::formatForDepth(uint8_t depth) const +{ + xcb_format_iterator_t iterator = + xcb_setup_pixmap_formats_iterator(m_setup); + + while (iterator.rem) { + xcb_format_t *format = iterator.data; + if (format->depth == depth) + return format; + xcb_format_next(&iterator); + } + + return 0; +} + void QXcbConnection::sync() { // from xcb_aux_sync diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h index b01937a139..01014775b9 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.h +++ b/src/plugins/platforms/xcb/qxcbconnection.h @@ -247,6 +247,8 @@ public: const char *displayName() const { return m_displayName.constData(); } xcb_connection_t *xcb_connection() const { return m_connection; } + const xcb_setup_t *setup() const { return m_setup; } + const xcb_format_t *formatForDepth(uint8_t depth) const; QXcbKeyboard *keyboard() const { return m_keyboard; } diff --git a/src/plugins/platforms/xcb/qxcbintegration.cpp b/src/plugins/platforms/xcb/qxcbintegration.cpp index 2c89bd6e80..81b6cda840 100644 --- a/src/plugins/platforms/xcb/qxcbintegration.cpp +++ b/src/plugins/platforms/xcb/qxcbintegration.cpp @@ -125,14 +125,218 @@ QPlatformFontDatabase *QXcbIntegration::fontDatabase() const return m_fontDatabase; } +static QImage::Format imageFormatForVisual(QXcbConnection *connection, uint8_t depth, const xcb_visualtype_t *visual) +{ + const xcb_format_t *format = connection->formatForDepth(depth); + + if (!visual || !format) + return QImage::Format_Invalid; + + if (depth == 32 && format->bits_per_pixel == 32 && visual->red_mask == 0xff0000 + && visual->green_mask == 0xff00 && visual->blue_mask == 0xff) + return QImage::Format_ARGB32_Premultiplied; + + if (depth == 24 && format->bits_per_pixel == 32 && visual->red_mask == 0xff0000 + && visual->green_mask == 0xff00 && visual->blue_mask == 0xff) + return QImage::Format_RGB32; + + if (depth == 16 && format->bits_per_pixel == 16 && visual->red_mask == 0xf800 + && visual->green_mask == 0x7e0 && visual->blue_mask == 0x1f) + return QImage::Format_RGB16; + + return QImage::Format_Invalid; +} + QPixmap QXcbIntegration::grabWindow(WId window, int x, int y, int width, int height) const { - Q_UNUSED(window); - Q_UNUSED(x); - Q_UNUSED(y); - Q_UNUSED(width); - Q_UNUSED(height); - return QPixmap(); + if (width == 0 || height == 0) + return QPixmap(); + + xcb_connection_t *connection = m_connection->xcb_connection(); + + xcb_get_geometry_cookie_t geometry_cookie = xcb_get_geometry(connection, window); + + xcb_generic_error_t *error; + xcb_get_geometry_reply_t *reply = + xcb_get_geometry_reply(connection, geometry_cookie, &error); + + if (!reply) { + if (error) { + m_connection->handleXcbError(error); + free(error); + } + return QPixmap(); + } + + if (width < 0) + width = reply->width - x; + if (height < 0) + height = reply->height - y; + + // TODO: handle multiple screens + QXcbScreen *screen = m_connection->screens().at(0); + xcb_window_t root = screen->root(); + geometry_cookie = xcb_get_geometry(connection, root); + xcb_get_geometry_reply_t *root_reply = + xcb_get_geometry_reply(connection, geometry_cookie, &error); + + if (!root_reply) { + if (error) { + m_connection->handleXcbError(error); + free(error); + } + free(reply); + return QPixmap(); + } + + if (reply->depth == root_reply->depth) { + // if the depth of the specified window and the root window are the + // same, grab pixels from the root window (so that we get the any + // overlapping windows and window manager frames) + + // map x and y to the root window + xcb_translate_coordinates_cookie_t translate_cookie = + xcb_translate_coordinates(connection, window, root, x, y); + + xcb_translate_coordinates_reply_t *translate_reply = + xcb_translate_coordinates_reply(connection, translate_cookie, &error); + + if (!translate_reply) { + if (error) { + m_connection->handleXcbError(error); + free(error); + } + free(reply); + free(root_reply); + return QPixmap(); + } + + x = translate_reply->dst_x; + y = translate_reply->dst_y; + + window = root; + free(reply); + reply = root_reply; + } else { + free(root_reply); + root_reply = 0; + } + + xcb_get_window_attributes_reply_t *attributes_reply = + xcb_get_window_attributes_reply(connection, xcb_get_window_attributes(connection, window), &error); + + if (!attributes_reply) { + if (error) { + m_connection->handleXcbError(error); + free(error); + } + free(reply); + return QPixmap(); + } + + const xcb_visualtype_t *visual = screen->visualForId(attributes_reply->visual); + free(attributes_reply); + + xcb_pixmap_t pixmap = xcb_generate_id(connection); + error = xcb_request_check(connection, xcb_create_pixmap_checked(connection, reply->depth, pixmap, window, width, height)); + if (error) { + m_connection->handleXcbError(error); + free(error); + } + + uint32_t gc_value_mask = XCB_GC_SUBWINDOW_MODE; + uint32_t gc_value_list[] = { XCB_SUBWINDOW_MODE_INCLUDE_INFERIORS }; + + xcb_gcontext_t gc = xcb_generate_id(connection); + xcb_create_gc(connection, gc, pixmap, gc_value_mask, gc_value_list); + + error = xcb_request_check(connection, xcb_copy_area_checked(connection, window, pixmap, gc, x, y, 0, 0, width, height)); + if (error) { + m_connection->handleXcbError(error); + free(error); + } + + xcb_get_image_cookie_t get_image_cookie = + xcb_get_image(connection, XCB_IMAGE_FORMAT_Z_PIXMAP, pixmap, 0, 0, width, height, 0xffffffff); + + xcb_get_image_reply_t *image_reply = + xcb_get_image_reply(connection, get_image_cookie, &error); + + xcb_free_gc(connection, gc); + xcb_free_pixmap(connection, pixmap); + + uint8_t depth = reply->depth; + + free(reply); + + if (!image_reply) { + if (error) { + m_connection->handleXcbError(error); + free(error); + } + return QPixmap(); + } + + uint8_t *data = xcb_get_image_data(image_reply); + uint32_t length = xcb_get_image_data_length(image_reply); + + QPixmap result; + + QImage::Format format = imageFormatForVisual(m_connection, depth, visual); + if (format != QImage::Format_Invalid) { + uint32_t bytes_per_line = length / height; + QImage image(const_cast(data), width, height, bytes_per_line, format); + uint8_t image_byte_order = m_connection->setup()->image_byte_order; + + // we may have to swap the byte order + if ((QSysInfo::ByteOrder == QSysInfo::LittleEndian && image_byte_order == XCB_IMAGE_ORDER_MSB_FIRST) + || (QSysInfo::ByteOrder == QSysInfo::BigEndian && image_byte_order == XCB_IMAGE_ORDER_LSB_FIRST)) + { + for (int i=0; i < image.height(); i++) { + switch (format) { + case QImage::Format_RGB16: { + ushort *p = (ushort*)image.scanLine(i); + ushort *end = p + image.width(); + while (p < end) { + *p = ((*p << 8) & 0xff00) | ((*p >> 8) & 0x00ff); + p++; + } + break; + } + case QImage::Format_RGB32: // fall-through + case QImage::Format_ARGB32_Premultiplied: { + uint *p = (uint*)image.scanLine(i); + uint *end = p + image.width(); + while (p < end) { + *p = ((*p << 24) & 0xff000000) | ((*p << 8) & 0x00ff0000) + | ((*p >> 8) & 0x0000ff00) | ((*p >> 24) & 0x000000ff); + p++; + } + break; + } + default: + Q_ASSERT(false); + } + } + } + + // fix-up alpha channel + if (format == QImage::Format_RGB32) { + QRgb *p = (QRgb *)image.bits(); + for (int y = 0; y < height; ++y) { + for (int x = 0; x < width; ++x) + p[x] |= 0xff000000; + p += bytes_per_line / 4; + } + } + + image.detach(); + result = QPixmap::fromImage(image); + } + + free(image_reply); + + return result; } diff --git a/src/plugins/platforms/xcb/qxcbscreen.cpp b/src/plugins/platforms/xcb/qxcbscreen.cpp index 7aef0275e3..1b6bf30294 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.cpp +++ b/src/plugins/platforms/xcb/qxcbscreen.cpp @@ -122,12 +122,37 @@ QXcbScreen::QXcbScreen(QXcbConnection *connection, xcb_screen_t *screen, int num 32, 1, &m_clientLeader), connection); + + xcb_depth_iterator_t depth_iterator = + xcb_screen_allowed_depths_iterator(screen); + + while (depth_iterator.rem) { + xcb_depth_t *depth = depth_iterator.data; + xcb_visualtype_iterator_t visualtype_iterator = + xcb_depth_visuals_iterator(depth); + + while (visualtype_iterator.rem) { + xcb_visualtype_t *visualtype = visualtype_iterator.data; + m_visuals.insert(visualtype->visual_id, *visualtype); + xcb_visualtype_next(&visualtype_iterator); + } + + xcb_depth_next(&depth_iterator); + } } QXcbScreen::~QXcbScreen() { } +const xcb_visualtype_t *QXcbScreen::visualForId(xcb_visualid_t visualid) const +{ + QMap::const_iterator it = m_visuals.find(visualid); + if (it == m_visuals.constEnd()) + return 0; + return &*it; +} + QRect QXcbScreen::geometry() const { return QRect(0, 0, m_screen->width_in_pixels, m_screen->height_in_pixels); diff --git a/src/plugins/platforms/xcb/qxcbscreen.h b/src/plugins/platforms/xcb/qxcbscreen.h index 38ec52de0a..d2a38b0791 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.h +++ b/src/plugins/platforms/xcb/qxcbscreen.h @@ -72,12 +72,15 @@ public: QString windowManagerName() const { return m_windowManagerName; } bool syncRequestSupported() const { return m_syncRequestSupported; } + const xcb_visualtype_t *visualForId(xcb_visualid_t) const; + private: xcb_screen_t *m_screen; int m_number; QString m_windowManagerName; bool m_syncRequestSupported; xcb_window_t m_clientLeader; + QMap m_visuals; }; #endif -- cgit v1.2.3