From 23a372ab8d59cd9b33356a16744313890b544f4f Mon Sep 17 00:00:00 2001 From: Uli Schlachter Date: Sun, 15 Jan 2012 17:45:14 +0100 Subject: xcb: Obey maximum request length when uploading images MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When connecting to an X11 server, the server tells the client what its maximum allowed request size is. Larger requests are not permitted. Thus, libxcb verifies that all requests which are sent are smaller than this size limit and possibly kills the connection (without any good error message). Thus, when uploading an image, we could be trying to send more pixel data than fit into a single request. If this would be the case, the code will now use multiple requests where each request only sends a part of the allowed rows. In case all the data fits into a single request, this commit shouldn't change any behavior. Change-Id: I84a4ebfcdb6de7e206015c37e3e33cba3bd309b1 Signed-off-by: Uli Schlachter Reviewed-by: Robin Burchell Reviewed-by: Samuel Rødal --- src/plugins/platforms/xcb/qxcbbackingstore.cpp | 52 ++++++++++++++++++++------ 1 file changed, 41 insertions(+), 11 deletions(-) (limited to 'src/plugins/platforms/xcb') diff --git a/src/plugins/platforms/xcb/qxcbbackingstore.cpp b/src/plugins/platforms/xcb/qxcbbackingstore.cpp index 4348ddbae9..ad8b47c25a 100644 --- a/src/plugins/platforms/xcb/qxcbbackingstore.cpp +++ b/src/plugins/platforms/xcb/qxcbbackingstore.cpp @@ -182,17 +182,47 @@ void QXcbShmImage::put(xcb_window_t window, const QPoint &target, const QRect &s source.height(), false); } else { - xcb_image_t *subimage = xcb_image_subimage(m_xcb_image, source.x(), source.y(), source.width(), source.height(), - 0, 0, 0); - xcb_image_put(xcb_connection(), - window, - m_gc, - subimage, - target.x(), - target.y(), - 0); - - xcb_image_destroy(subimage); + // If we upload the whole image in a single chunk, the result might be + // larger than the server's maximum request size and stuff breaks. + // To work around that, we upload the image in chunks where each chunk + // is small enough for a single request. + int src_x = source.x(); + int src_y = source.y(); + int target_x = target.x(); + int target_y = target.y(); + int width = source.width(); + int height = source.height(); + + // We must make sure that each request is not larger than max_req_size. + // Each request takes req_size + m_xcb_image->stride * height bytes. + uint32_t max_req_size = xcb_get_maximum_request_length(xcb_connection()); + uint32_t req_size = sizeof(xcb_put_image_request_t); + int rows_per_put = (max_req_size - req_size) / m_xcb_image->stride; + + // This assert could trigger if a single row has more pixels than fit in + // a single PutImage request. However, max_req_size is guaranteed to be + // at least 16384 bytes. That should be enough for quite large images. + Q_ASSERT(rows_per_put > 0); + + while (height > 0) { + int rows = std::min(height, rows_per_put); + + xcb_image_t *subimage = xcb_image_subimage(m_xcb_image, src_x, src_y, width, rows, + 0, 0, 0); + xcb_image_put(xcb_connection(), + window, + m_gc, + subimage, + target_x, + target_y, + 0); + + xcb_image_destroy(subimage); + + src_y += rows; + target_y += rows; + height -= rows; + } } Q_XCB_NOOP(connection()); -- cgit v1.2.3