#include <QtDebug>
#include <QScreen>
+#include <QtGui/QIcon>
+#include <QtGui/QRegion>
#include "qxcbconnection.h"
#include "qxcbscreen.h"
#include "qxcbdrag.h"
#include "qxcbkeyboard.h"
#include "qxcbwmsupport.h"
+#include "qxcbimage.h"
#include <qpa/qplatformintegration.h>
#include <xcb/xcb_icccm.h>
#undef class
#include <xcb/xfixes.h>
+#ifndef QT_NO_SHAPE
+# include <xcb/shape.h>
+#endif // QT_NO_SHAPE
// xcb-icccm 3.8 support
#ifdef XCB_ICCCM_NUM_WM_SIZE_HINTS_ELEMENTS
#include <private/qwindow_p.h>
#include <qpa/qplatformbackingstore.h>
-#include <QtGui/QWindowSystemInterface>
+#include <qpa/qwindowsysteminterface.h>
#include <stdio.h>
#include <X11/Xutil.h>
#endif
-#ifdef XCB_USE_XINPUT2_MAEMO
+#if defined(XCB_USE_XINPUT2_MAEMO) || defined(XCB_USE_XINPUT2)
#include <X11/extensions/XInput2.h>
#endif
}
}
+static inline bool positionIncludesFrame(QWindow *w)
+{
+ return qt_window_private(w)->positionPolicy == QWindowPrivate::WindowFrameInclusive;
+}
+
QXcbWindow::QXcbWindow(QWindow *window)
: QPlatformWindow(window)
, m_window(0)
, m_syncCounter(0)
+ , m_gravity(XCB_GRAVITY_STATIC)
, m_mapped(false)
, m_transparent(false)
, m_deferredActivation(false)
, m_netWmUserTimeWindow(XCB_NONE)
+ , m_dirtyFrameMargins(false)
#if defined(XCB_USE_EGL)
, m_eglSurface(0)
#endif
return;
}
+ // Determine gravity from initial position. Do not change
+ // later as it will cause the window to move uncontrollably.
+ m_gravity = positionIncludesFrame(window()) ?
+ XCB_GRAVITY_NORTH_WEST : XCB_GRAVITY_STATIC;
+
const quint32 mask = XCB_CW_BACK_PIXMAP | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_SAVE_UNDER | XCB_CW_EVENT_MASK;
const quint32 values[] = {
// XCB_CW_BACK_PIXMAP
| XCB_EVENT_MASK_FOCUS_CHANGE
};
+ // Parameters to XCreateWindow() are frame corner + inner size.
+ // This fits in case position policy is frame inclusive. There is
+ // currently no way to implement it for frame-exclusive geometries.
QRect rect = window()->geometry();
QPlatformWindow::setGeometry(rect);
{
#if defined(XCB_USE_GLX)
XVisualInfo *visualInfo = qglx_findVisualInfo(DISPLAY_FROM_XCB(m_screen), m_screen->screenNumber(), &m_format);
- if (!visualInfo)
+ if (!visualInfo && window()->surfaceType() == QSurface::OpenGLSurface)
qFatal("Could not initialize GLX");
#elif defined(XCB_USE_EGL)
EGLDisplay eglDisplay = connection()->egl_display();
XVisualInfo *visualInfo;
int matchingCount = 0;
visualInfo = XGetVisualInfo(DISPLAY_FROM_XCB(this), VisualIDMask, &visualInfoTemplate, &matchingCount);
- if (!visualInfo)
+ if (!visualInfo && window()->surfaceType() == QSurface::OpenGLSurface)
qFatal("Could not initialize EGL");
#endif //XCB_USE_GLX
- m_depth = visualInfo->depth;
- m_imageFormat = imageFormatForDepth(m_depth);
- Colormap cmap = XCreateColormap(DISPLAY_FROM_XCB(this), xcb_parent_id, visualInfo->visual, AllocNone);
+ if (visualInfo) {
+ m_depth = visualInfo->depth;
+ m_imageFormat = imageFormatForDepth(m_depth);
+ Colormap cmap = XCreateColormap(DISPLAY_FROM_XCB(this), xcb_parent_id, visualInfo->visual, AllocNone);
- XSetWindowAttributes a;
- a.background_pixel = WhitePixel(DISPLAY_FROM_XCB(this), m_screen->screenNumber());
- a.border_pixel = BlackPixel(DISPLAY_FROM_XCB(this), m_screen->screenNumber());
- a.colormap = cmap;
+ XSetWindowAttributes a;
+ a.background_pixel = WhitePixel(DISPLAY_FROM_XCB(this), m_screen->screenNumber());
+ a.border_pixel = BlackPixel(DISPLAY_FROM_XCB(this), m_screen->screenNumber());
+ a.colormap = cmap;
- m_visualId = visualInfo->visualid;
+ m_visualId = visualInfo->visualid;
- m_window = XCreateWindow(DISPLAY_FROM_XCB(this), xcb_parent_id, rect.x(), rect.y(), rect.width(), rect.height(),
- 0, visualInfo->depth, InputOutput, visualInfo->visual,
- CWBackPixel|CWBorderPixel|CWColormap, &a);
+ m_window = XCreateWindow(DISPLAY_FROM_XCB(this), xcb_parent_id, rect.x(), rect.y(), rect.width(), rect.height(),
+ 0, visualInfo->depth, InputOutput, visualInfo->visual,
+ CWBackPixel|CWBorderPixel|CWColormap, &a);
- XFree(visualInfo);
- } else
+ XFree(visualInfo);
+ }
+ }
+
+ if (!m_window)
#endif //defined(XCB_USE_GLX) || defined(XCB_USE_EGL)
{
m_window = xcb_generate_id(xcb_connection());
1, &leader));
#ifdef XCB_USE_XINPUT2_MAEMO
- if (connection()->isUsingXInput2()) {
+ if (connection()->isUsingXInput2Maemo()) {
XIEventMask xieventmask;
uchar bitmask[2] = { 0, 0 };
XISelectEvents(DISPLAY_FROM_XCB(this), m_window, &xieventmask, 1);
}
+#elif defined(XCB_USE_XINPUT2)
+ connection()->xi2Select(m_window);
#endif
setWindowFlags(window()->windowFlags());
if (window()->windowFlags() & Qt::WindowTransparentForInput)
setTransparentForMouseEvents(true);
+#ifndef QT_NO_DRAGANDDROP
connection()->drag()->dndEnable(this, true);
+#endif
}
QXcbWindow::~QXcbWindow()
}
connection()->removeWindow(m_window);
Q_XCB_CALL(xcb_destroy_window(xcb_connection(), m_window));
+ m_window = 0;
}
m_mapped = false;
QPlatformWindow::setGeometry(rect);
propagateSizeHints();
+ const QRect wmGeometry = windowToWmGeometry(rect);
const quint32 mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
const qint32 values[] = {
- qBound<qint32>(-XCOORD_MAX, rect.x(), XCOORD_MAX),
- qBound<qint32>(-XCOORD_MAX, rect.y(), XCOORD_MAX),
- qBound<qint32>(1, rect.width(), XCOORD_MAX),
- qBound<qint32>(1, rect.height(), XCOORD_MAX),
+ qBound<qint32>(-XCOORD_MAX, wmGeometry.x(), XCOORD_MAX),
+ qBound<qint32>(-XCOORD_MAX, wmGeometry.y(), XCOORD_MAX),
+ qBound<qint32>(1, wmGeometry.width(), XCOORD_MAX),
+ qBound<qint32>(1, wmGeometry.height(), XCOORD_MAX),
};
Q_XCB_CALL(xcb_configure_window(xcb_connection(), m_window, mask, reinterpret_cast<const quint32*>(values)));
ba.constData()));
}
+void QXcbWindow::setWindowIcon(const QIcon &icon)
+{
+ QVector<quint32> icon_data;
+
+ if (!icon.isNull()) {
+ QList<QSize> availableSizes = icon.availableSizes();
+ if (availableSizes.isEmpty()) {
+ // try to use default sizes since the icon can be a scalable image like svg.
+ availableSizes.push_back(QSize(16,16));
+ availableSizes.push_back(QSize(32,32));
+ availableSizes.push_back(QSize(64,64));
+ availableSizes.push_back(QSize(128,128));
+ }
+ for (int i = 0; i < availableSizes.size(); ++i) {
+ QSize size = availableSizes.at(i);
+ QPixmap pixmap = icon.pixmap(size);
+ if (!pixmap.isNull()) {
+ QImage image = pixmap.toImage().convertToFormat(QImage::Format_ARGB32);
+ int pos = icon_data.size();
+ icon_data.resize(pos + 2 + image.width()*image.height());
+ icon_data[pos++] = image.width();
+ icon_data[pos++] = image.height();
+ memcpy(icon_data.data() + pos, image.bits(), image.width()*image.height()*4);
+ }
+ }
+ }
+
+ if (!icon_data.isEmpty()) {
+ Q_XCB_CALL(xcb_change_property(xcb_connection(),
+ XCB_PROP_MODE_REPLACE,
+ m_window,
+ atom(QXcbAtom::_NET_WM_ICON),
+ atom(QXcbAtom::CARDINAL),
+ 32,
+ icon_data.size(),
+ (unsigned char *) icon_data.data()));
+ } else {
+ Q_XCB_CALL(xcb_delete_property(xcb_connection(),
+ m_window,
+ atom(QXcbAtom::_NET_WM_ICON)));
+ }
+}
+
void QXcbWindow::raise()
{
const quint32 mask = XCB_CONFIG_WINDOW_STACK_MODE;
Q_XCB_CALL(xcb_configure_window(xcb_connection(), m_window, mask, values));
}
+// Adapt the geometry to match the WM expection with regards
+// to gravity.
+QRect QXcbWindow::windowToWmGeometry(QRect r) const
+{
+ if (m_dirtyFrameMargins || m_frameMargins.isNull())
+ return r;
+ const bool frameInclusive = positionIncludesFrame(window());
+ // XCB_GRAVITY_STATIC requires the inner geometry, whereas
+ // XCB_GRAVITY_NORTH_WEST requires the frame geometry
+ if (frameInclusive && m_gravity == XCB_GRAVITY_STATIC) {
+ r.translate(m_frameMargins.left(), m_frameMargins.top());
+ } else if (!frameInclusive && m_gravity == XCB_GRAVITY_NORTH_WEST) {
+ r.translate(-m_frameMargins.left(), -m_frameMargins.top());
+ }
+ return r;
+}
+
void QXcbWindow::propagateSizeHints()
{
// update WM_NORMAL_HINTS
xcb_size_hints_t hints;
memset(&hints, 0, sizeof(hints));
- QRect rect = geometry();
+ const QRect rect = windowToWmGeometry(geometry());
QWindow *win = window();
xcb_size_hints_set_position(&hints, true, rect.x(), rect.y());
xcb_size_hints_set_size(&hints, true, rect.width(), rect.height());
- xcb_size_hints_set_win_gravity(&hints, qt_window_private(win)->positionPolicy == QWindowPrivate::WindowFrameInclusive
- ? XCB_GRAVITY_NORTH_WEST : XCB_GRAVITY_STATIC);
+ xcb_size_hints_set_win_gravity(&hints, m_gravity);
QSize minimumSize = win->minimumSize();
QSize maximumSize = win->maximumSize();
} else {
qWarning() << "QXcbWindow: Unhandled WM_PROTOCOLS message:" << connection()->atomName(event->data.data32[0]);
}
+#ifndef QT_NO_DRAGANDDROP
} else if (event->type == atom(QXcbAtom::XdndEnter)) {
connection()->drag()->handleEnter(window(), event);
} else if (event->type == atom(QXcbAtom::XdndPosition)) {
connection()->drag()->handleLeave(window(), event);
} else if (event->type == atom(QXcbAtom::XdndDrop)) {
connection()->drag()->handleDrop(window(), event);
+#endif
} else if (event->type == atom(QXcbAtom::_XEMBED)) { // QSystemTrayIcon
} else {
qWarning() << "QXcbWindow: Unhandled client message:" << connection()->atomName(event->type);
xcb_flush(xcb_connection());
}
+bool QXcbWindow::startSystemResize(const QPoint &pos, Qt::Corner corner)
+{
+ const xcb_atom_t moveResize = connection()->atom(QXcbAtom::_NET_WM_MOVERESIZE);
+ if (!connection()->wmSupport()->isSupportedByWM(moveResize))
+ return false;
+ xcb_client_message_event_t xev;
+ xev.response_type = XCB_CLIENT_MESSAGE;
+ xev.type = moveResize;
+ xev.window = xcb_window();
+ xev.format = 32;
+ const QPoint globalPos = window()->mapToGlobal(pos);
+ xev.data.data32[0] = globalPos.x();
+ xev.data.data32[1] = globalPos.y();
+ const bool bottom = corner == Qt::BottomRightCorner || corner == Qt::BottomLeftCorner;
+ const bool left = corner == Qt::BottomLeftCorner || corner == Qt::TopLeftCorner;
+ if (bottom)
+ xev.data.data32[2] = left ? 6 : 4; // bottomleft/bottomright
+ else
+ xev.data.data32[2] = left ? 0 : 2; // topleft/topright
+ xev.data.data32[3] = XCB_BUTTON_INDEX_1;
+ xev.data.data32[4] = 0;
+ xcb_ungrab_pointer(connection()->xcb_connection(), XCB_CURRENT_TIME);
+ xcb_send_event(connection()->xcb_connection(), false, m_screen->root(),
+ XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY,
+ (const char *)&xev);
+ return true;
+}
+
+#if !defined(QT_NO_SHAPE)
+
+static inline xcb_rectangle_t qRectToXCBRectangle(const QRect &r)
+{
+ xcb_rectangle_t result;
+ result.x = qMax(SHRT_MIN, r.x());
+ result.y = qMax(SHRT_MIN, r.y());
+ result.width = qMin((int)USHRT_MAX, r.width());
+ result.height = qMin((int)USHRT_MAX, r.height());
+ return result;
+}
+
+void QXcbWindow::setMask(const QRegion ®ion)
+{
+ if (!connection()->hasXShape())
+ return;
+ if (region.isEmpty()) {
+ xcb_shape_mask(connection()->xcb_connection(), XCB_SHAPE_SO_SET,
+ XCB_SHAPE_SK_BOUNDING, xcb_window(), 0, 0, XCB_NONE);
+ } else {
+ QVector<xcb_rectangle_t> rects;
+ foreach (const QRect &r, region.rects())
+ rects.push_back(qRectToXCBRectangle(r));
+ xcb_shape_rectangles(connection()->xcb_connection(), XCB_SHAPE_SO_SET,
+ XCB_SHAPE_SK_BOUNDING, XCB_CLIP_ORDERING_UNSORTED,
+ xcb_window(), 0, 0, rects.size(), &rects[0]);
+ }
+}
+
+#endif // !QT_NO_SHAPE
+
QT_END_NAMESPACE