Implemented QXcbIntegration::grabWindow().

bb10
Samuel Rødal 2011-06-03 13:39:12 +02:00
parent 1f456b4cbb
commit e99ab7de9b
5 changed files with 255 additions and 6 deletions

View File

@ -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

View File

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

View File

@ -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<uint8_t *>(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;
}

View File

@ -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<xcb_visualid_t, xcb_visualtype_t>::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);

View File

@ -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<xcb_visualid_t, xcb_visualtype_t> m_visuals;
};
#endif