summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/xcb
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/platforms/xcb')
-rw-r--r--src/plugins/platforms/xcb/main.cpp8
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection.cpp33
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection.h15
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection_xi2.cpp290
-rw-r--r--src/plugins/platforms/xcb/qxcbintegration.cpp41
-rw-r--r--src/plugins/platforms/xcb/qxcbintegration.h7
-rw-r--r--src/plugins/platforms/xcb/qxcbkeyboard.cpp8
-rw-r--r--src/plugins/platforms/xcb/qxcbnativeinterface.cpp109
-rw-r--r--src/plugins/platforms/xcb/qxcbnativeinterface.h13
-rw-r--r--src/plugins/platforms/xcb/qxcbscreen.cpp3
-rw-r--r--src/plugins/platforms/xcb/qxcbsessionmanager.cpp512
-rw-r--r--src/plugins/platforms/xcb/qxcbsessionmanager.h88
-rw-r--r--src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp180
-rw-r--r--src/plugins/platforms/xcb/qxcbsystemtraytracker.h87
-rw-r--r--src/plugins/platforms/xcb/qxcbwindow.cpp31
-rw-r--r--src/plugins/platforms/xcb/xcb-plugin.pro13
16 files changed, 1249 insertions, 189 deletions
diff --git a/src/plugins/platforms/xcb/main.cpp b/src/plugins/platforms/xcb/main.cpp
index be5a0e3501..e114827703 100644
--- a/src/plugins/platforms/xcb/main.cpp
+++ b/src/plugins/platforms/xcb/main.cpp
@@ -47,15 +47,15 @@ QT_BEGIN_NAMESPACE
class QXcbIntegrationPlugin : public QPlatformIntegrationPlugin
{
Q_OBJECT
- Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QPA.QPlatformIntegrationFactoryInterface.5.1" FILE "xcb.json")
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QPA.QPlatformIntegrationFactoryInterface.5.2" FILE "xcb.json")
public:
- QPlatformIntegration *create(const QString&, const QStringList&);
+ QPlatformIntegration *create(const QString&, const QStringList&, int &, char **);
};
-QPlatformIntegration* QXcbIntegrationPlugin::create(const QString& system, const QStringList& parameters)
+QPlatformIntegration* QXcbIntegrationPlugin::create(const QString& system, const QStringList& parameters, int &argc, char **argv)
{
if (system.toLower() == "xcb")
- return new QXcbIntegration(parameters);
+ return new QXcbIntegration(parameters, argc, argv);
return 0;
}
diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp
index 2ce34ea8f2..ff5b36c448 100644
--- a/src/plugins/platforms/xcb/qxcbconnection.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection.cpp
@@ -51,13 +51,15 @@
#include "qxcbwmsupport.h"
#include "qxcbnativeinterface.h"
#include "qxcbintegration.h"
+#include "qxcbsystemtraytracker.h"
-#include <QtAlgorithms>
#include <QSocketNotifier>
#include <QAbstractEventDispatcher>
#include <QTimer>
#include <QByteArray>
+#include <algorithm>
+
#include <dlfcn.h>
#include <stdio.h>
#include <errno.h>
@@ -262,6 +264,7 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra
, has_xkb(false)
, m_buttons(0)
, m_focusWindow(0)
+ , m_systemTrayTracker(0)
{
#ifdef XCB_USE_XLIB
Display *dpy = XOpenDisplay(m_displayName.constData());
@@ -656,6 +659,11 @@ void QXcbConnection::log(const char *file, int line, int sequence)
void QXcbConnection::handleXcbError(xcb_generic_error_t *error)
{
+ long result = 0;
+ QAbstractEventDispatcher* dispatcher = QAbstractEventDispatcher::instance();
+ if (dispatcher && dispatcher->filterNativeEvent(m_nativeInterface->genericEventFilterType(), error, &result))
+ return;
+
uint clamped_error_code = qMin<uint>(error->error_code, (sizeof(xcb_errors) / sizeof(xcb_errors[0])) - 1);
uint clamped_major_code = qMin<uint>(error->major_code, (sizeof(xcb_protocol_request_codes) / sizeof(xcb_protocol_request_codes[0])) - 1);
@@ -813,6 +821,8 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
HANDLE_PLATFORM_WINDOW_EVENT(xcb_map_notify_event_t, event, handleMapNotifyEvent);
case XCB_UNMAP_NOTIFY:
HANDLE_PLATFORM_WINDOW_EVENT(xcb_unmap_notify_event_t, event, handleUnmapNotifyEvent);
+ case XCB_DESTROY_NOTIFY:
+ HANDLE_PLATFORM_WINDOW_EVENT(xcb_destroy_notify_event_t, event, handleDestroyNotifyEvent);
case XCB_CLIENT_MESSAGE:
handleClientMessageEvent((xcb_client_message_event_t *)event);
break;
@@ -1196,6 +1206,8 @@ void QXcbConnection::handleClientMessageEvent(const xcb_client_message_event_t *
drag()->handleFinished(event);
}
#endif
+ if (m_systemTrayTracker && event->type == atom(QXcbAtom::MANAGER))
+ m_systemTrayTracker->notifyManagerClientMessageEvent(event);
QXcbWindow *window = platformWindowFromId(event->window);
if (!window)
@@ -1231,6 +1243,8 @@ static const char * xcb_atomnames = {
"_NET_WM_CONTEXT_HELP\0"
"_NET_WM_SYNC_REQUEST\0"
"_NET_WM_SYNC_REQUEST_COUNTER\0"
+ "MANAGER\0"
+ "_NET_SYSTEM_TRAY_OPCODE\0"
// ICCCM window state
"WM_STATE\0"
@@ -1388,6 +1402,8 @@ static const char * xcb_atomnames = {
"Abs MT Pressure\0"
"Abs MT Tracking ID\0"
"Max Contacts\0"
+ "Rel X\0"
+ "Rel Y\0"
// XInput2 tablet
"Abs X\0"
"Abs Y\0"
@@ -1404,9 +1420,9 @@ static const char * xcb_atomnames = {
"_XSETTINGS_SETTINGS\0" // \0\0 terminates loop.
};
-xcb_atom_t QXcbConnection::atom(QXcbAtom::Atom atom)
+QXcbAtom::Atom QXcbConnection::qatom(xcb_atom_t xatom) const
{
- return m_allAtoms[atom];
+ return static_cast<QXcbAtom::Atom>(std::find(m_allAtoms, m_allAtoms + QXcbAtom::NAtoms, xatom) - m_allAtoms);
}
void QXcbConnection::initializeAllAtoms() {
@@ -1724,6 +1740,17 @@ bool QXcbConnection::xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t *event, int o
}
#endif // defined(XCB_USE_XINPUT2) || defined(XCB_USE_XINPUT2_MAEMO)
+QXcbSystemTrayTracker *QXcbConnection::systemTrayTracker()
+{
+ if (!m_systemTrayTracker) {
+ if ( (m_systemTrayTracker = QXcbSystemTrayTracker::create(this)) ) {
+ connect(m_systemTrayTracker, SIGNAL(systemTrayWindowChanged(QScreen*)),
+ QGuiApplication::platformNativeInterface(), SIGNAL(systemTrayWindowChanged(QScreen*)));
+ }
+ }
+ return m_systemTrayTracker;
+}
+
QXcbConnectionGrabber::QXcbConnectionGrabber(QXcbConnection *connection)
:m_connection(connection)
{
diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h
index 2a5ff0b1cb..7cabe67a68 100644
--- a/src/plugins/platforms/xcb/qxcbconnection.h
+++ b/src/plugins/platforms/xcb/qxcbconnection.h
@@ -87,6 +87,7 @@ class QXcbKeyboard;
class QXcbClipboard;
class QXcbWMSupport;
class QXcbNativeInterface;
+class QXcbSystemTrayTracker;
namespace QXcbAtom {
enum Atom {
@@ -98,6 +99,8 @@ namespace QXcbAtom {
_NET_WM_CONTEXT_HELP,
_NET_WM_SYNC_REQUEST,
_NET_WM_SYNC_REQUEST_COUNTER,
+ MANAGER, // System tray notification
+ _NET_SYSTEM_TRAY_OPCODE, // System tray operation
// ICCCM window state
WM_STATE,
@@ -256,6 +259,8 @@ namespace QXcbAtom {
AbsMTPressure,
AbsMTTrackingID,
MaxContacts,
+ RelX,
+ RelY,
// XInput2 tablet
AbsX,
AbsY,
@@ -319,6 +324,7 @@ public:
virtual void handleConfigureNotifyEvent(const xcb_configure_notify_event_t *) {}
virtual void handleMapNotifyEvent(const xcb_map_notify_event_t *) {}
virtual void handleUnmapNotifyEvent(const xcb_unmap_notify_event_t *) {}
+ virtual void handleDestroyNotifyEvent(const xcb_destroy_notify_event_t *) {}
virtual void handleButtonPressEvent(const xcb_button_press_event_t *) {}
virtual void handleButtonReleaseEvent(const xcb_button_release_event_t *) {}
virtual void handleMotionNotifyEvent(const xcb_motion_notify_event_t *) {}
@@ -346,7 +352,8 @@ public:
const QList<QXcbScreen *> &screens() const { return m_screens; }
int primaryScreen() const { return m_primaryScreen; }
- xcb_atom_t atom(QXcbAtom::Atom atom);
+ inline xcb_atom_t atom(QXcbAtom::Atom atom) const { return m_allAtoms[atom]; }
+ QXcbAtom::Atom qatom(xcb_atom_t atom) const;
xcb_atom_t internAtom(const char *name);
QByteArray atomName(xcb_atom_t atom);
@@ -430,6 +437,9 @@ public:
void ungrabServer();
QXcbNativeInterface *nativeInterface() const { return m_nativeInterface; }
+
+ QXcbSystemTrayTracker *systemTrayTracker();
+
private slots:
void processXcbEvents();
@@ -478,8 +488,6 @@ private:
};
QHash<int, ValuatorClassInfo> valuatorInfo;
};
- void xi2QueryTabletData(void *dev, TabletData *tabletData); // use no XI stuff in headers
- void xi2SetupTabletDevices();
bool xi2HandleTabletEvent(void *event, TabletData *tabletData);
void xi2ReportTabletEvent(const TabletData &tabletData, void *event);
QVector<TabletData> m_tabletData;
@@ -565,6 +573,7 @@ private:
QXcbWindow *m_focusWindow;
QByteArray m_startupId;
+ QXcbSystemTrayTracker *m_systemTrayTracker;
};
#define DISPLAY_FROM_XCB(object) ((Display *)(object->connection()->xlib_display()))
diff --git a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
index 799bb387e1..b144d953a7 100644
--- a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
@@ -44,8 +44,8 @@
#include "qxcbwindow.h"
#include "qtouchdevice.h"
#include <qpa/qwindowsysteminterface.h>
-//#define XI2_TOUCH_DEBUG
-#ifdef XI2_TOUCH_DEBUG
+//#define XI2_DEBUG
+#ifdef XI2_DEBUG
#include <QDebug>
#endif
@@ -63,20 +63,19 @@ struct XInput2DeviceData {
}
XIDeviceInfo *xiDeviceInfo;
QTouchDevice *qtTouchDevice;
-};
-#ifndef QT_NO_TABLETEVENT
-static inline bool q_xi2_is_tablet(XIDeviceInfo *dev)
-{
- QByteArray name(dev->name);
- name = name.toLower();
- // Cannot just check for "wacom" because that would also pick up the touch and tablet-button devices.
- return name.contains("stylus") || name.contains("eraser");
-}
-#endif // QT_NO_TABLETEVENT
+ // Stuff that is relevant only for touchpads
+ QHash<int, QPointF> pointPressedPosition; // in screen coordinates where each point was pressed
+ QPointF firstPressedPosition; // in screen coordinates where the first point was pressed
+ QPointF firstPressedNormalPosition; // device coordinates (0 to 1, 0 to 1) where the first point was pressed
+ QSizeF size; // device size in mm
+};
void QXcbConnection::initializeXInput2()
{
+#ifndef QT_NO_TABLETEVENT
+ m_tabletData.clear();
+#endif
Display *xDisplay = static_cast<Display *>(m_xlib_display);
if (XQueryExtension(xDisplay, "XInputExtension", &m_xiOpCode, &m_xiEventBase, &m_xiErrorBase)) {
int xiMajor = 2;
@@ -88,13 +87,78 @@ void QXcbConnection::initializeXInput2()
m_xi2Enabled = true;
}
if (m_xi2Enabled) {
+#ifdef XI2_DEBUG
+ qDebug("XInput version %d.%d is supported", xiMajor, m_xi2Minor);
+#endif
+ int deviceCount = 0;
+ XIDeviceInfo *devices = XIQueryDevice(xDisplay, XIAllDevices, &deviceCount);
+ for (int i = 0; i < deviceCount; ++i) {
+ // Only non-master pointing devices are relevant here.
+ if (devices[i].use != XISlavePointer)
+ continue;
+#ifdef XI2_DEBUG
+ qDebug() << "input device "<< devices[i].name;
+#endif
#ifndef QT_NO_TABLETEVENT
- // Tablet support: Find the stylus-related devices.
- xi2SetupTabletDevices();
+ TabletData tabletData;
+#endif
+ for (int c = 0; c < devices[i].num_classes; ++c) {
+ switch (devices[i].classes[c]->type) {
+ case XIValuatorClass: {
+ XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(devices[i].classes[c]);
+ const int valuatorAtom = qatom(vci->label);
+#ifdef XI2_DEBUG
+ qDebug() << " has valuator" << atomName(vci->label) << "recognized?" << (valuatorAtom < QXcbAtom::NAtoms);
+#endif
+#ifndef QT_NO_TABLETEVENT
+ if (valuatorAtom < QXcbAtom::NAtoms) {
+ TabletData::ValuatorClassInfo info;
+ info.minVal = vci->min;
+ info.maxVal = vci->max;
+ info.number = vci->number;
+ tabletData.valuatorInfo[valuatorAtom] = info;
+ }
#endif // QT_NO_TABLETEVENT
-#ifdef XI2_TOUCH_DEBUG
- qDebug("XInput version %d.%d is supported", xiMajor, m_xi2Minor);
+ } break;
+ default:
+ break;
+ }
+ }
+ bool isTablet = false;
+#ifndef QT_NO_TABLETEVENT
+ // If we have found the valuators which we expect a tablet to have, assume it's a tablet.
+ if (tabletData.valuatorInfo.contains(QXcbAtom::AbsX) &&
+ tabletData.valuatorInfo.contains(QXcbAtom::AbsY) &&
+ tabletData.valuatorInfo.contains(QXcbAtom::AbsPressure)) {
+ tabletData.deviceId = devices[i].deviceid;
+ tabletData.pointerType = QTabletEvent::Pen;
+ if (QByteArray(devices[i].name).toLower().contains("eraser"))
+ tabletData.pointerType = QTabletEvent::Eraser;
+ m_tabletData.append(tabletData);
+ isTablet = true;
+#ifdef XI2_DEBUG
+ qDebug() << " it's a tablet with pointer type" << tabletData.pointerType;
#endif
+ }
+#endif // QT_NO_TABLETEVENT
+ if (!isTablet) {
+ XInput2DeviceData *dev = deviceForId(devices[i].deviceid);
+#ifdef XI2_DEBUG
+ if (dev && dev->qtTouchDevice->type() == QTouchDevice::TouchScreen)
+ qDebug(" it's a touchscreen with type %d capabilities 0x%X max touch points %d",
+ dev->qtTouchDevice->type(), (unsigned int)dev->qtTouchDevice->capabilities(),
+ dev->qtTouchDevice->maximumTouchPoints());
+ else if (dev && dev->qtTouchDevice->type() == QTouchDevice::TouchPad)
+ qDebug(" it's a touchpad with type %d capabilities 0x%X max touch points %d size %f x %f",
+ dev->qtTouchDevice->type(), (unsigned int)dev->qtTouchDevice->capabilities(),
+ dev->qtTouchDevice->maximumTouchPoints(),
+ dev->size.width(), dev->size.height());
+#else
+ Q_UNUSED(dev);
+#endif // XI2_DEBUG
+ }
+ }
+ XIFreeDeviceInfo(devices);
}
}
}
@@ -113,25 +177,24 @@ void QXcbConnection::xi2Select(xcb_window_t window)
unsigned char *xiBitMask = reinterpret_cast<unsigned char *>(&bitMask);
#ifdef XCB_USE_XINPUT22
- // Select touch events on all master devices indiscriminately.
bitMask |= XI_TouchBeginMask;
bitMask |= XI_TouchUpdateMask;
bitMask |= XI_TouchEndMask;
XIEventMask mask;
- mask.deviceid = XIAllMasterDevices;
mask.mask_len = sizeof(bitMask);
mask.mask = xiBitMask;
- Status result = XISelectEvents(xDisplay, window, &mask, 1);
- // If we have XInput 2.2 and successfully enable touch on the master
- // devices, then evdev touchscreens will provide touch only. In most other
- // cases, there will be emulated mouse events, because true X11 touch
- // support is so new that for the older drivers, mouse emulation was the
- // only way; and it's still the fallback even with the modern evdev driver.
- // But if neither Qt nor X11 does mouse emulation, it will not be possible
- // to interact with mouse-oriented QWidgets; so we have to let Qt do it.
- if (m_xi2Minor >= 2 && result == Success)
- has_touch_without_mouse_emulation = true;
-#endif
+ // Enable each touchscreen
+ foreach (XInput2DeviceData *dev, m_touchDevices.values()) {
+ mask.deviceid = dev->xiDeviceInfo->deviceid;
+ Status result = XISelectEvents(xDisplay, window, &mask, 1);
+ // If we have XInput >= 2.2 and successfully enable a touchscreen, then
+ // it will provide touch only. In most other cases, there will be
+ // emulated mouse events from the driver. If not, then Qt must do its
+ // own mouse emulation to enable interaction with mouse-oriented QWidgets.
+ if (m_xi2Minor >= 2 && result == Success)
+ has_touch_without_mouse_emulation = true;
+ }
+#endif // XCB_USE_XINPUT22
#ifndef QT_NO_TABLETEVENT
// For each tablet, select some additional event types.
@@ -163,23 +226,29 @@ XInput2DeviceData *QXcbConnection::deviceForId(int id)
QTouchDevice::Capabilities caps = 0;
dev = new XInput2DeviceData;
dev->xiDeviceInfo = XIQueryDevice(static_cast<Display *>(m_xlib_display), id, &unused);
- dev->qtTouchDevice = new QTouchDevice;
+ int type = -1;
+ int maxTouchPoints = 1;
+ bool hasRelativeCoords = false;
for (int i = 0; i < dev->xiDeviceInfo->num_classes; ++i) {
XIAnyClassInfo *classinfo = dev->xiDeviceInfo->classes[i];
switch (classinfo->type) {
#ifdef XCB_USE_XINPUT22
case XITouchClass: {
XITouchClassInfo *tci = reinterpret_cast<XITouchClassInfo *>(classinfo);
+ maxTouchPoints = tci->num_touches;
+#ifdef XI2_DEBUG
+ qDebug(" has touch class with mode %d", tci->mode);
+#endif
switch (tci->mode) {
case XIModeRelative:
- dev->qtTouchDevice->setType(QTouchDevice::TouchPad);
+ type = QTouchDevice::TouchPad;
break;
case XIModeAbsolute:
- dev->qtTouchDevice->setType(QTouchDevice::TouchScreen);
+ type = QTouchDevice::TouchScreen;
break;
}
} break;
-#endif
+#endif // XCB_USE_XINPUT22
case XIValuatorClass: {
XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(classinfo);
if (vci->label == atom(QXcbAtom::AbsMTPositionX))
@@ -188,18 +257,36 @@ XInput2DeviceData *QXcbConnection::deviceForId(int id)
caps |= QTouchDevice::Area;
else if (vci->label == atom(QXcbAtom::AbsMTPressure) || vci->label == atom(QXcbAtom::AbsPressure))
caps |= QTouchDevice::Pressure;
+ else if (vci->label == atom(QXcbAtom::RelX)) {
+ hasRelativeCoords = true;
+ dev->size.setWidth((vci->max - vci->min) * 1000.0 / vci->resolution);
+ } else if (vci->label == atom(QXcbAtom::RelY)) {
+ hasRelativeCoords = true;
+ dev->size.setHeight((vci->max - vci->min) * 1000.0 / vci->resolution);
+ }
} break;
}
}
- dev->qtTouchDevice->setCapabilities(caps);
- dev->qtTouchDevice->setName(dev->xiDeviceInfo->name);
- if (caps != 0)
- QWindowSystemInterface::registerTouchDevice(dev->qtTouchDevice);
-#ifdef XI2_TOUCH_DEBUG
- qDebug("registered new device %s with %d classes and %d max touch points",
- dev->xiDeviceInfo->name, dev->xiDeviceInfo->num_classes, dev->qtTouchDevice->maxTouchPoints());
-#endif
- m_touchDevices[id] = dev;
+ if (type < 0 && caps && hasRelativeCoords) {
+ type = QTouchDevice::TouchPad;
+ if (dev->size.width() < 10 || dev->size.height() < 10 ||
+ dev->size.width() > 10000 || dev->size.height() > 10000)
+ dev->size = QSizeF(130, 110);
+ }
+ if (type >= QTouchDevice::TouchScreen && type <= QTouchDevice::TouchPad) {
+ dev->qtTouchDevice = new QTouchDevice;
+ dev->qtTouchDevice->setName(dev->xiDeviceInfo->name);
+ dev->qtTouchDevice->setType((QTouchDevice::DeviceType)type);
+ dev->qtTouchDevice->setCapabilities(caps);
+ dev->qtTouchDevice->setMaximumTouchPoints(maxTouchPoints);
+ if (caps != 0)
+ QWindowSystemInterface::registerTouchDevice(dev->qtTouchDevice);
+ m_touchDevices[id] = dev;
+ } else {
+ m_touchDevices.remove(id);
+ delete dev;
+ dev = 0;
+ }
}
return dev;
}
@@ -218,7 +305,7 @@ static qreal valuatorNormalized(double value, XIValuatorClassInfo *vci)
value = vci->min;
return (value - vci->min) / (vci->max - vci->min);
}
-#endif
+#endif // XCB_USE_XINPUT22
void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event)
{
@@ -237,7 +324,7 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event)
#ifdef XCB_USE_XINPUT22
if (xiEvent->evtype == XI_TouchBegin || xiEvent->evtype == XI_TouchUpdate || xiEvent->evtype == XI_TouchEnd) {
xXIDeviceEvent* xiDeviceEvent = reinterpret_cast<xXIDeviceEvent *>(event);
-#ifdef XI2_TOUCH_DEBUG
+#ifdef XI2_DEBUG
qDebug("XI2 event type %d seq %d detail %d pos 0x%X,0x%X %f,%f root pos %f,%f",
event->event_type, xiEvent->sequenceNumber, xiDeviceEvent->detail,
xiDeviceEvent->event_x, xiDeviceEvent->event_y,
@@ -247,6 +334,8 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event)
if (QXcbWindow *platformWindow = platformWindowFromId(xiDeviceEvent->event)) {
XInput2DeviceData *dev = deviceForId(xiEvent->deviceid);
+ Q_ASSERT(dev);
+ const bool firstTouch = m_touchPoints.isEmpty();
if (xiEvent->evtype == XI_TouchBegin) {
QWindowSystemInterface::TouchPoint tp;
tp.id = xiDeviceEvent->detail % INT_MAX;
@@ -267,11 +356,15 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event)
double value;
if (!xi2GetValuatorValueIfSet(xiDeviceEvent, n, &value))
continue;
-#ifdef XI2_TOUCH_DEBUG
- qDebug(" valuator class label %d value %lf from range %lf -> %lf name %s",
- vci->label, value, vci->min, vci->max, XGetAtomName(static_cast<Display *>(m_xlib_display), vci->label) );
+#ifdef XI2_DEBUG
+ qDebug(" valuator %20s value %lf from range %lf -> %lf",
+ atomName(vci->label).constData(), value, vci->min, vci->max );
#endif
- if (vci->label == atom(QXcbAtom::AbsMTPositionX)) {
+ if (vci->label == atom(QXcbAtom::RelX)) {
+ nx = valuatorNormalized(value, vci);
+ } else if (vci->label == atom(QXcbAtom::RelY)) {
+ ny = valuatorNormalized(value, vci);
+ } else if (vci->label == atom(QXcbAtom::AbsMTPositionX)) {
nx = valuatorNormalized(value, vci);
} else if (vci->label == atom(QXcbAtom::AbsMTPositionY)) {
ny = valuatorNormalized(value, vci);
@@ -306,17 +399,43 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event)
}
switch (xiEvent->evtype) {
+ case XI_TouchBegin:
+ if (firstTouch) {
+ dev->firstPressedPosition = QPointF(x, y);
+ dev->firstPressedNormalPosition = QPointF(nx, ny);
+ }
+ dev->pointPressedPosition.insert(touchPoint.id, QPointF(x, y));
+ break;
case XI_TouchUpdate:
- if (touchPoint.area.center() != QPoint(x, y))
+ if (dev->qtTouchDevice->type() == QTouchDevice::TouchPad && dev->pointPressedPosition.value(touchPoint.id) == QPointF(x, y)) {
+ qreal dx = (nx - dev->firstPressedNormalPosition.x()) *
+ dev->size.width() * screen->geometry().width() / screen->physicalSize().width();
+ qreal dy = (ny - dev->firstPressedNormalPosition.y()) *
+ dev->size.height() * screen->geometry().height() / screen->physicalSize().height();
+ x = dev->firstPressedPosition.x() + dx;
+ y = dev->firstPressedPosition.y() + dy;
+ touchPoint.state = Qt::TouchPointMoved;
+ } else if (touchPoint.area.center() != QPoint(x, y)) {
touchPoint.state = Qt::TouchPointMoved;
+ dev->pointPressedPosition[touchPoint.id] = QPointF(x, y);
+ }
break;
case XI_TouchEnd:
touchPoint.state = Qt::TouchPointReleased;
+ if (dev->qtTouchDevice->type() == QTouchDevice::TouchPad && dev->pointPressedPosition.value(touchPoint.id) == QPointF(x, y)) {
+ qreal dx = (nx - dev->firstPressedNormalPosition.x()) *
+ dev->size.width() * screen->geometry().width() / screen->physicalSize().width();
+ qreal dy = (ny - dev->firstPressedNormalPosition.y()) *
+ dev->size.width() * screen->geometry().width() / screen->physicalSize().width();
+ x = dev->firstPressedPosition.x() + dx;
+ y = dev->firstPressedPosition.y() + dy;
+ }
+ dev->pointPressedPosition.remove(touchPoint.id);
}
touchPoint.area = QRectF(x - w/2, y - h/2, w, h);
touchPoint.normalPosition = QPointF(nx, ny);
-#ifdef XI2_TOUCH_DEBUG
+#ifdef XI2_DEBUG
qDebug() << " tp " << touchPoint.id << " state " << touchPoint.state << " pos norm " << touchPoint.normalPosition <<
" area " << touchPoint.area << " pressure " << touchPoint.pressure;
#endif
@@ -330,80 +449,11 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event)
touchPoint.state = Qt::TouchPointStationary;
}
}
-#endif
+#endif // XCB_USE_XINPUT22
}
}
#ifndef QT_NO_TABLETEVENT
-void QXcbConnection::xi2QueryTabletData(void *dev, TabletData *tabletData)
-{
- XIDeviceInfo *device = static_cast<XIDeviceInfo *>(dev);
- tabletData->deviceId = device->deviceid;
-
- tabletData->pointerType = QTabletEvent::Pen;
- if (QByteArray(device->name).toLower().contains("eraser"))
- tabletData->pointerType = QTabletEvent::Eraser;
-
- for (int i = 0; i < device->num_classes; ++i) {
- switch (device->classes[i]->type) {
- case XIValuatorClass: {
- XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(device->classes[i]);
- int val = 0;
- if (vci->label == atom(QXcbAtom::AbsX))
- val = QXcbAtom::AbsX;
- else if (vci->label == atom(QXcbAtom::AbsY))
- val = QXcbAtom::AbsY;
- else if (vci->label == atom(QXcbAtom::AbsPressure))
- val = QXcbAtom::AbsPressure;
- else if (vci->label == atom(QXcbAtom::AbsTiltX))
- val = QXcbAtom::AbsTiltX;
- else if (vci->label == atom(QXcbAtom::AbsTiltY))
- val = QXcbAtom::AbsTiltY;
- else if (vci->label == atom(QXcbAtom::AbsWheel))
- val = QXcbAtom::AbsWheel;
- else if (vci->label == atom(QXcbAtom::AbsDistance))
- val = QXcbAtom::AbsDistance;
- if (val) {
- TabletData::ValuatorClassInfo info;
- info.minVal = vci->min;
- info.maxVal = vci->max;
- info.number = vci->number;
- tabletData->valuatorInfo[val] = info;
- }
- }
- break;
- default:
- break;
- }
- }
-}
-
-void QXcbConnection::xi2SetupTabletDevices()
-{
- Display *xDisplay = static_cast<Display *>(m_xlib_display);
- m_tabletData.clear();
- int deviceCount = 0;
- XIDeviceInfo *devices = XIQueryDevice(xDisplay, XIAllDevices, &deviceCount);
- if (devices) {
- for (int i = 0; i < deviceCount; ++i) {
- int unused = 0;
- XIDeviceInfo *dev = XIQueryDevice(xDisplay, devices[i].deviceid, &unused);
- if (dev) {
- if (q_xi2_is_tablet(dev)) {
- TabletData tabletData;
- xi2QueryTabletData(dev, &tabletData);
-#ifdef XI2_TOUCH_DEBUG
- qDebug() << "found tablet" << dev->name;
-#endif
- m_tabletData.append(tabletData);
- }
- XIFreeDeviceInfo(dev);
- }
- }
- XIFreeDeviceInfo(devices);
- }
-}
-
bool QXcbConnection::xi2HandleTabletEvent(void *event, TabletData *tabletData)
{
bool handled = true;
diff --git a/src/plugins/platforms/xcb/qxcbintegration.cpp b/src/plugins/platforms/xcb/qxcbintegration.cpp
index cf7e99023a..e10c556473 100644
--- a/src/plugins/platforms/xcb/qxcbintegration.cpp
+++ b/src/plugins/platforms/xcb/qxcbintegration.cpp
@@ -50,6 +50,10 @@
#include "qxcbclipboard.h"
#include "qxcbdrag.h"
+#ifndef QT_NO_SESSIONMANAGER
+#include "qxcbsessionmanager.h"
+#endif
+
#include <xcb/xcb.h>
#include <QtPlatformSupport/private/qgenericunixeventdispatcher_p.h>
@@ -119,9 +123,10 @@ static bool runningUnderDebugger()
}
#endif
-QXcbIntegration::QXcbIntegration(const QStringList &parameters)
+QXcbIntegration::QXcbIntegration(const QStringList &parameters, int &argc, char **argv)
: m_eventDispatcher(createUnixEventDispatcher())
, m_services(new QGenericUnixServices)
+ , m_instanceName(0)
{
QGuiApplicationPrivate::instance()->setEventDispatcher(m_eventDispatcher);
@@ -138,7 +143,28 @@ QXcbIntegration::QXcbIntegration(const QStringList &parameters)
if (canNotGrabEnv)
canGrab = false;
- m_connections << new QXcbConnection(m_nativeInterface.data(), canGrab);
+ // Parse arguments
+ const char *displayName = 0;
+ if (argc) {
+ int j = 1;
+ for (int i = 1; i < argc; i++) {
+ char *arg = argv[i];
+ if (arg) {
+ if (!strcmp(arg, "-display") && i < argc - 1) {
+ displayName = argv[++i];
+ arg = 0;
+ } else if (!strcmp(arg, "-name") && i < argc - 1) {
+ m_instanceName = argv[++i];
+ arg = 0;
+ }
+ }
+ if (arg)
+ argv[j++] = arg;
+ }
+ argc = j;
+ } // argc
+
+ m_connections << new QXcbConnection(m_nativeInterface.data(), canGrab, displayName);
for (int i = 0; i < parameters.size() - 1; i += 2) {
#ifdef Q_XCB_DEBUG
@@ -365,6 +391,8 @@ QVariant QXcbIntegration::styleHint(QPlatformIntegration::StyleHint hint) const
case QPlatformIntegration::SynthesizeMouseFromTouchEvents:
// We do not want Qt to synthesize mouse events if X11 already does it.
return m_connections.at(0)->hasTouchWithoutMouseEmulation();
+ default:
+ break;
}
return QPlatformIntegration::styleHint(hint);
}
@@ -389,6 +417,8 @@ QByteArray QXcbIntegration::wmClass() const
if (m_wmClass.isEmpty()) {
// Instance name according to ICCCM 4.1.2.5
QString name;
+ if (m_instanceName)
+ name = QString::fromLocal8Bit(m_instanceName);
if (name.isEmpty() && qEnvironmentVariableIsSet(resourceNameVar))
name = QString::fromLocal8Bit(qgetenv(resourceNameVar));
if (name.isEmpty())
@@ -413,4 +443,11 @@ QByteArray QXcbIntegration::wmClass() const
return m_wmClass;
}
+#ifndef QT_NO_SESSIONMANAGER
+QPlatformSessionManager *QXcbIntegration::createPlatformSessionManager(const QString &id, const QString &key) const
+{
+ return new QXcbSessionManager(id, key);
+}
+#endif
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/qxcbintegration.h b/src/plugins/platforms/xcb/qxcbintegration.h
index 07b6b8d678..7fd5756fd5 100644
--- a/src/plugins/platforms/xcb/qxcbintegration.h
+++ b/src/plugins/platforms/xcb/qxcbintegration.h
@@ -55,7 +55,7 @@ class QXcbScreen;
class QXcbIntegration : public QPlatformIntegration
{
public:
- QXcbIntegration(const QStringList &parameters);
+ QXcbIntegration(const QStringList &parameters, int &argc, char **argv);
~QXcbIntegration();
QPlatformWindow *createPlatformWindow(QWindow *window) const;
@@ -101,6 +101,10 @@ public:
QByteArray wmClass() const;
+#ifndef QT_NO_SESSIONMANAGER
+ QPlatformSessionManager *createPlatformSessionManager(const QString &id, const QString &key) const Q_DECL_OVERRIDE;
+#endif
+
private:
QList<QXcbConnection *> m_connections;
@@ -119,6 +123,7 @@ private:
friend class QXcbConnection; // access QPlatformIntegration::screenAdded()
mutable QByteArray m_wmClass;
+ const char *m_instanceName;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.cpp b/src/plugins/platforms/xcb/qxcbkeyboard.cpp
index d1729ed168..2529fb8a83 100644
--- a/src/plugins/platforms/xcb/qxcbkeyboard.cpp
+++ b/src/plugins/platforms/xcb/qxcbkeyboard.cpp
@@ -243,6 +243,10 @@
#define XF86XK_Select 0x1008FFA0
#define XF86XK_View 0x1008FFA1
#define XF86XK_TopMenu 0x1008FFA2
+#define XF86XK_Red 0x1008FFA3
+#define XF86XK_Green 0x1008FFA4
+#define XF86XK_Yellow 0x1008FFA5
+#define XF86XK_Blue 0x1008FFA6
#define XF86XK_Suspend 0x1008FFA7
#define XF86XK_Hibernate 0x1008FFA8
#define XF86XK_TouchpadToggle 0x1008FFA9
@@ -538,6 +542,10 @@ static const unsigned int KeyTbl[] = {
XF86XK_Select, Qt::Key_Select,
XF86XK_View, Qt::Key_View,
XF86XK_TopMenu, Qt::Key_TopMenu,
+ XF86XK_Red, Qt::Key_Red,
+ XF86XK_Green, Qt::Key_Green,
+ XF86XK_Yellow, Qt::Key_Yellow,
+ XF86XK_Blue, Qt::Key_Blue,
XF86XK_Bluetooth, Qt::Key_Bluetooth,
XF86XK_Suspend, Qt::Key_Suspend,
XF86XK_Hibernate, Qt::Key_Hibernate,
diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp
index 9e9fd2914f..72299d5a9a 100644
--- a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp
+++ b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp
@@ -42,7 +42,9 @@
#include "qxcbnativeinterface.h"
#include "qxcbscreen.h"
+#include "qxcbwindow.h"
#include "qxcbintegration.h"
+#include "qxcbsystemtraytracker.h"
#include <private/qguiapplication_p.h>
#include <QtCore/QMap>
@@ -64,28 +66,28 @@
# include <stdio.h>
#endif
+#include <algorithm>
+
QT_BEGIN_NAMESPACE
-class QXcbResourceMap : public QMap<QByteArray, QXcbNativeInterface::ResourceType>
+// return QXcbNativeInterface::ResourceType for the key.
+static int resourceType(const QByteArray &key)
{
-public:
- QXcbResourceMap()
- :QMap<QByteArray, QXcbNativeInterface::ResourceType>()
- {
- insert("display",QXcbNativeInterface::Display);
- insert("egldisplay",QXcbNativeInterface::EglDisplay);
- insert("connection",QXcbNativeInterface::Connection);
- insert("screen",QXcbNativeInterface::Screen);
- insert("eglcontext",QXcbNativeInterface::EglContext);
- insert("glxcontext",QXcbNativeInterface::GLXContext);
- insert("apptime",QXcbNativeInterface::AppTime);
- insert("appusertime",QXcbNativeInterface::AppUserTime);
- insert("hintstyle", QXcbNativeInterface::ScreenHintStyle);
- insert("startupid", QXcbNativeInterface::StartupId);
- }
-};
-
-Q_GLOBAL_STATIC(QXcbResourceMap, qXcbResourceMap)
+ static const QByteArray names[] = { // match QXcbNativeInterface::ResourceType
+ QByteArrayLiteral("display"), QByteArrayLiteral("egldisplay"),
+ QByteArrayLiteral("connection"), QByteArrayLiteral("screen"),
+ QByteArrayLiteral("graphicsdevice"), QByteArrayLiteral("eglcontext"),
+ QByteArrayLiteral("glxcontext"), QByteArrayLiteral("apptime"),
+ QByteArrayLiteral("appusertime"), QByteArrayLiteral("hintstyle"),
+ QByteArrayLiteral("startupid"), QByteArrayLiteral("traywindow"),
+ QByteArrayLiteral("gettimestamp")
+ };
+ const QByteArray *end = names + sizeof(names) / sizeof(names[0]);
+ const QByteArray *result = std::find(names, end, key);
+ if (result == end)
+ result = std::find(names, end, key.toLower());
+ return int(result - names);
+}
QXcbNativeInterface::QXcbNativeInterface() :
m_genericEventFilterType(QByteArrayLiteral("xcb_generic_event_t"))
@@ -100,15 +102,40 @@ void QXcbNativeInterface::beep() // For QApplication::beep()
xcb_bell(connection, 0);
}
-void *QXcbNativeInterface::nativeResourceForIntegration(const QByteArray &resourceString)
+static inline QXcbSystemTrayTracker *systemTrayTracker(const QScreen *s)
{
- QByteArray lowerCaseResource = resourceString.toLower();
- if (!qXcbResourceMap()->contains(lowerCaseResource))
- return 0;
+ return static_cast<const QXcbScreen *>(s->handle())->connection()->systemTrayTracker();
+}
- ResourceType resource = qXcbResourceMap()->value(lowerCaseResource);
+bool QXcbNativeInterface::systemTrayAvailable(const QScreen *screen) const
+{
+ return systemTrayTracker(screen);
+}
+
+bool QXcbNativeInterface::requestSystemTrayWindowDock(const QWindow *window)
+{
+ const QPlatformWindow *platformWindow = window->handle();
+ if (!platformWindow)
+ return false;
+ QXcbSystemTrayTracker *trayTracker = systemTrayTracker(window->screen());
+ if (!trayTracker)
+ return false;
+ trayTracker->requestSystemTrayWindowDock(static_cast<const QXcbWindow *>(platformWindow)->xcb_window());
+ return true;
+}
+
+QRect QXcbNativeInterface::systemTrayWindowGlobalGeometry(const QWindow *window)
+{
+ if (const QPlatformWindow *platformWindow = window->handle())
+ if (const QXcbSystemTrayTracker *trayTracker = systemTrayTracker(window->screen()))
+ return trayTracker->systemTrayWindowGlobalGeometry(static_cast<const QXcbWindow *>(platformWindow)->xcb_window());
+ return QRect();
+}
+
+void *QXcbNativeInterface::nativeResourceForIntegration(const QByteArray &resourceString)
+{
void *result = 0;
- switch (resource) {
+ switch (resourceType(resourceString)) {
case StartupId:
result = startupId();
break;
@@ -121,13 +148,8 @@ void *QXcbNativeInterface::nativeResourceForIntegration(const QByteArray &resour
void *QXcbNativeInterface::nativeResourceForContext(const QByteArray &resourceString, QOpenGLContext *context)
{
- QByteArray lowerCaseResource = resourceString.toLower();
- if (!qXcbResourceMap()->contains(lowerCaseResource))
- return 0;
-
- ResourceType resource = qXcbResourceMap()->value(lowerCaseResource);
void *result = 0;
- switch(resource) {
+ switch (resourceType(resourceString)) {
case EglContext:
result = eglContextForContext(context);
break;
@@ -143,12 +165,9 @@ void *QXcbNativeInterface::nativeResourceForContext(const QByteArray &resourceSt
void *QXcbNativeInterface::nativeResourceForScreen(const QByteArray &resource, QScreen *screen)
{
- const QXcbResourceMap::const_iterator it = qXcbResourceMap()->constFind(resource.toLower());
- if (it == qXcbResourceMap()->constEnd() || !screen->handle())
- return 0;
void *result = 0;
const QXcbScreen *xcbScreen = static_cast<QXcbScreen *>(screen->handle());
- switch (it.value()) {
+ switch (resourceType(resource)) {
case Display:
#ifdef XCB_USE_XLIB
result = xcbScreen->connection()->xlib_display();
@@ -162,6 +181,14 @@ void *QXcbNativeInterface::nativeResourceForScreen(const QByteArray &resource, Q
break;
case ScreenHintStyle:
result = reinterpret_cast<void *>(xcbScreen->hintStyle() + 1);
+ break;
+ case TrayWindow:
+ if (QXcbSystemTrayTracker *s = systemTrayTracker(screen))
+ result = (void *)quintptr(s->trayWindow());
+ break;
+ case GetTimestamp:
+ result = getTimestamp(xcbScreen);
+ break;
default:
break;
}
@@ -170,13 +197,8 @@ void *QXcbNativeInterface::nativeResourceForScreen(const QByteArray &resource, Q
void *QXcbNativeInterface::nativeResourceForWindow(const QByteArray &resourceString, QWindow *window)
{
- QByteArray lowerCaseResource = resourceString.toLower();
- if (!qXcbResourceMap()->contains(lowerCaseResource))
- return 0;
-
- ResourceType resource = qXcbResourceMap()->value(lowerCaseResource);
void *result = 0;
- switch(resource) {
+ switch (resourceType(resourceString)) {
case Display:
result = displayForWindow(window);
break;
@@ -216,6 +238,11 @@ void *QXcbNativeInterface::appUserTime(const QXcbScreen *screen)
return reinterpret_cast<void *>(quintptr(screen->connection()->netWmUserTime()));
}
+void *QXcbNativeInterface::getTimestamp(const QXcbScreen *screen)
+{
+ return reinterpret_cast<void *>(quintptr(screen->connection()->getTimestamp()));
+}
+
void *QXcbNativeInterface::startupId()
{
QXcbIntegration* integration = static_cast<QXcbIntegration *>(QGuiApplicationPrivate::platformIntegration());
diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.h b/src/plugins/platforms/xcb/qxcbnativeinterface.h
index e27bfa5a46..86b94e62e4 100644
--- a/src/plugins/platforms/xcb/qxcbnativeinterface.h
+++ b/src/plugins/platforms/xcb/qxcbnativeinterface.h
@@ -45,6 +45,8 @@
#include <qpa/qplatformnativeinterface.h>
#include <xcb/xcb.h>
+#include <QtCore/QRect>
+
QT_BEGIN_NAMESPACE
class QWidget;
@@ -66,7 +68,9 @@ public:
AppTime,
AppUserTime,
ScreenHintStyle,
- StartupId
+ StartupId,
+ TrayWindow,
+ GetTimestamp
};
QXcbNativeInterface();
@@ -88,6 +92,7 @@ public:
void *graphicsDeviceForWindow(QWindow *window);
void *appTime(const QXcbScreen *screen);
void *appUserTime(const QXcbScreen *screen);
+ void *getTimestamp(const QXcbScreen *screen);
void *startupId();
static void setAppTime(QScreen *screen, xcb_timestamp_t time);
static void setAppUserTime(QScreen *screen, xcb_timestamp_t time);
@@ -95,6 +100,12 @@ public:
static void *glxContextForContext(QOpenGLContext *context);
Q_INVOKABLE void beep();
+ Q_INVOKABLE bool systemTrayAvailable(const QScreen *screen) const;
+ Q_INVOKABLE bool requestSystemTrayWindowDock(const QWindow *window);
+ Q_INVOKABLE QRect systemTrayWindowGlobalGeometry(const QWindow *window);
+
+signals:
+ void systemTrayWindowChanged(QScreen *screen);
private:
const QByteArray m_genericEventFilterType;
diff --git a/src/plugins/platforms/xcb/qxcbscreen.cpp b/src/plugins/platforms/xcb/qxcbscreen.cpp
index 37c6c97bc4..6961a2fd79 100644
--- a/src/plugins/platforms/xcb/qxcbscreen.cpp
+++ b/src/plugins/platforms/xcb/qxcbscreen.cpp
@@ -112,6 +112,7 @@ QXcbScreen::QXcbScreen(QXcbConnection *connection, xcb_screen_t *scr,
XCB_EVENT_MASK_ENTER_WINDOW
| XCB_EVENT_MASK_LEAVE_WINDOW
| XCB_EVENT_MASK_PROPERTY_CHANGE
+ | XCB_EVENT_MASK_STRUCTURE_NOTIFY // for the "MANAGER" atom (system tray notification).
};
xcb_change_window_attributes(xcb_connection(), screen()->root, mask, values);
@@ -148,7 +149,7 @@ QXcbScreen::QXcbScreen(QXcbConnection *connection, xcb_screen_t *scr,
if (!sync_reply || !sync_reply->present)
m_syncRequestSupported = false;
else
- m_syncRequestSupported = m_windowManagerName != QLatin1String("KWin");
+ m_syncRequestSupported = true;
m_clientLeader = xcb_generate_id(xcb_connection());
Q_XCB_CALL2(xcb_create_window(xcb_connection(),
diff --git a/src/plugins/platforms/xcb/qxcbsessionmanager.cpp b/src/plugins/platforms/xcb/qxcbsessionmanager.cpp
new file mode 100644
index 0000000000..6abe24b7ab
--- /dev/null
+++ b/src/plugins/platforms/xcb/qxcbsessionmanager.cpp
@@ -0,0 +1,512 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Teo Mrnjavac <teo@kde.org>
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qxcbsessionmanager.h"
+
+#include <qguiapplication.h>
+#include <qdatetime.h>
+#include <qfileinfo.h>
+#include <qplatformdefs.h>
+#include <qsocketnotifier.h>
+#include <X11/SM/SMlib.h>
+
+
+class QSmSocketReceiver : public QObject
+{
+ Q_OBJECT
+public:
+ QSmSocketReceiver(int socket)
+ {
+ QSocketNotifier* sn = new QSocketNotifier(socket, QSocketNotifier::Read, this);
+ connect(sn, SIGNAL(activated(int)), this, SLOT(socketActivated(int)));
+ }
+
+public Q_SLOTS:
+ void socketActivated(int);
+};
+
+
+static SmcConn smcConnection = 0;
+static bool sm_interactionActive;
+static bool sm_smActive;
+static int sm_interactStyle;
+static int sm_saveType;
+static bool sm_cancel;
+static bool sm_waitingForInteraction;
+static bool sm_isshutdown;
+static bool sm_phase2;
+static bool sm_in_phase2;
+bool qt_sm_blockUserInput = false;
+
+static QSmSocketReceiver* sm_receiver = 0;
+
+static void resetSmState();
+static void sm_setProperty(const char *name, const char *type,
+ int num_vals, SmPropValue *vals);
+static void sm_saveYourselfCallback(SmcConn smcConn, SmPointer clientData,
+ int saveType, Bool shutdown , int interactStyle, Bool fast);
+static void sm_saveYourselfPhase2Callback(SmcConn smcConn, SmPointer clientData) ;
+static void sm_dieCallback(SmcConn smcConn, SmPointer clientData) ;
+static void sm_shutdownCancelledCallback(SmcConn smcConn, SmPointer clientData);
+static void sm_saveCompleteCallback(SmcConn smcConn, SmPointer clientData);
+static void sm_interactCallback(SmcConn smcConn, SmPointer clientData);
+static void sm_performSaveYourself(QXcbSessionManager*);
+
+static void resetSmState()
+{
+ sm_waitingForInteraction = false;
+ sm_interactionActive = false;
+ sm_interactStyle = SmInteractStyleNone;
+ sm_smActive = false;
+ qt_sm_blockUserInput = false;
+ sm_isshutdown = false;
+ sm_phase2 = false;
+ sm_in_phase2 = false;
+}
+
+
+// theoretically it's possible to set several properties at once. For
+// simplicity, however, we do just one property at a time
+static void sm_setProperty(const char *name, const char *type,
+ int num_vals, SmPropValue *vals)
+{
+ if (num_vals) {
+ SmProp prop;
+ prop.name = const_cast<char*>(name);
+ prop.type = const_cast<char*>(type);
+ prop.num_vals = num_vals;
+ prop.vals = vals;
+
+ SmProp* props[1];
+ props[0] = &prop;
+ SmcSetProperties(smcConnection, 1, props);
+ } else {
+ char* names[1];
+ names[0] = const_cast<char*>(name);
+ SmcDeleteProperties(smcConnection, 1, names);
+ }
+}
+
+static void sm_setProperty(const QString &name, const QString &value)
+{
+ QByteArray v = value.toUtf8();
+ SmPropValue prop;
+ prop.length = v.length();
+ prop.value = (SmPointer) v.constData();
+ sm_setProperty(name.toLatin1().data(), SmARRAY8, 1, &prop);
+}
+
+static void sm_setProperty(const QString &name, const QStringList &value)
+{
+ SmPropValue *prop = new SmPropValue[value.count()];
+ int count = 0;
+ QList<QByteArray> vl;
+ for (QStringList::ConstIterator it = value.begin(); it != value.end(); ++it) {
+ prop[count].length = (*it).length();
+ vl.append((*it).toUtf8());
+ prop[count].value = (char*)vl.last().data();
+ ++count;
+ }
+ sm_setProperty(name.toLatin1().data(), SmLISTofARRAY8, count, prop);
+ delete [] prop;
+}
+
+
+// workaround for broken libsm, see below
+struct QT_smcConn {
+ unsigned int save_yourself_in_progress : 1;
+ unsigned int shutdown_in_progress : 1;
+};
+
+static void sm_saveYourselfCallback(SmcConn smcConn, SmPointer clientData,
+ int saveType, Bool shutdown , int interactStyle, Bool /*fast*/)
+{
+ if (smcConn != smcConnection)
+ return;
+ sm_cancel = false;
+ sm_smActive = true;
+ sm_isshutdown = shutdown;
+ sm_saveType = saveType;
+ sm_interactStyle = interactStyle;
+
+ // ugly workaround for broken libSM. libSM should do that _before_
+ // actually invoking the callback in sm_process.c
+ ((QT_smcConn*)smcConn)->save_yourself_in_progress = true;
+ if (sm_isshutdown)
+ ((QT_smcConn*)smcConn)->shutdown_in_progress = true;
+
+ sm_performSaveYourself((QXcbSessionManager*) clientData);
+ if (!sm_isshutdown) // we cannot expect a confirmation message in that case
+ resetSmState();
+}
+
+static void sm_performSaveYourself(QXcbSessionManager *sm)
+{
+ if (sm_isshutdown)
+ qt_sm_blockUserInput = true;
+
+ // generate a new session key
+ timeval tv;
+ gettimeofday(&tv, 0);
+ sm->setSessionKey(QString::number(qulonglong(tv.tv_sec)) +
+ QLatin1Char('_') +
+ QString::number(qulonglong(tv.tv_usec)));
+
+ QStringList arguments = QCoreApplication::arguments();
+ QString argument0 = arguments.isEmpty() ? QCoreApplication::applicationFilePath()
+ : arguments.at(0);
+
+ // tell the session manager about our program in best POSIX style
+ sm_setProperty(QString::fromLatin1(SmProgram), argument0);
+ // tell the session manager about our user as well.
+ struct passwd *entryPtr = 0;
+#if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && (_POSIX_THREAD_SAFE_FUNCTIONS - 0 > 0)
+ QVarLengthArray<char, 1024> buf(qMax<long>(sysconf(_SC_GETPW_R_SIZE_MAX), 1024L));
+ struct passwd entry;
+ while (getpwuid_r(geteuid(), &entry, buf.data(), buf.size(), &entryPtr) == ERANGE) {
+ if (buf.size() >= 32768) {
+ // too big already, fail
+ static char badusername[] = "";
+ entryPtr = &entry;
+ entry.pw_name = badusername;
+ break;
+ }
+
+ // retry with a bigger buffer
+ buf.resize(buf.size() * 2);
+ }
+#else
+ entryPtr = getpwuid(geteuid());
+#endif
+ if (entryPtr)
+ sm_setProperty(QString::fromLatin1(SmUserID), QString::fromLocal8Bit(entryPtr->pw_name));
+
+ // generate a restart and discard command that makes sense
+ QStringList restart;
+ restart << argument0 << QLatin1String("-session")
+ << sm->sessionId() + QLatin1Char('_') + sm->sessionKey();
+
+ QFileInfo fi = QCoreApplication::applicationFilePath();
+ if (qAppName().compare(fi.fileName(), Qt::CaseInsensitive) != 0)
+ restart << QLatin1String("-name") << qAppName();
+ sm->setRestartCommand(restart);
+ QStringList discard;
+ sm->setDiscardCommand(discard);
+
+ switch (sm_saveType) {
+ case SmSaveBoth:
+ sm->appCommitData();
+ if (sm_isshutdown && sm_cancel)
+ break; // we cancelled the shutdown, no need to save state
+ // fall through
+ case SmSaveLocal:
+ sm->appSaveState();
+ break;
+ case SmSaveGlobal:
+ sm->appCommitData();
+ break;
+ default:
+ break;
+ }
+
+ if (sm_phase2 && !sm_in_phase2) {
+ SmcRequestSaveYourselfPhase2(smcConnection, sm_saveYourselfPhase2Callback, (SmPointer*) sm);
+ qt_sm_blockUserInput = false;
+ } else {
+ // close eventual interaction monitors and cancel the
+ // shutdown, if required. Note that we can only cancel when
+ // performing a shutdown, it does not work for checkpoints
+ if (sm_interactionActive) {
+ SmcInteractDone(smcConnection, sm_isshutdown && sm_cancel);
+ sm_interactionActive = false;
+ } else if (sm_cancel && sm_isshutdown) {
+ if (sm->allowsErrorInteraction()) {
+ SmcInteractDone(smcConnection, True);
+ sm_interactionActive = false;
+ }
+ }
+
+ // set restart and discard command in session manager
+ sm_setProperty(QString::fromLatin1(SmRestartCommand), sm->restartCommand());
+ sm_setProperty(QString::fromLatin1(SmDiscardCommand), sm->discardCommand());
+
+ // set the restart hint
+ SmPropValue prop;
+ prop.length = sizeof(int);
+ int value = sm->restartHint();
+ prop.value = (SmPointer) &value;
+ sm_setProperty(SmRestartStyleHint, SmCARD8, 1, &prop);
+
+ // we are done
+ SmcSaveYourselfDone(smcConnection, !sm_cancel);
+ }
+}
+
+static void sm_dieCallback(SmcConn smcConn, SmPointer /* clientData */)
+{
+ if (smcConn != smcConnection)
+ return;
+ resetSmState();
+ QEvent quitEvent(QEvent::Quit);
+ QGuiApplication::sendEvent(qApp, &quitEvent);
+}
+
+static void sm_shutdownCancelledCallback(SmcConn smcConn, SmPointer clientData)
+{
+ if (smcConn != smcConnection)
+ return;
+ if (sm_waitingForInteraction)
+ ((QXcbSessionManager *) clientData)->exitEventLoop();
+ resetSmState();
+}
+
+static void sm_saveCompleteCallback(SmcConn smcConn, SmPointer /*clientData */)
+{
+ if (smcConn != smcConnection)
+ return;
+ resetSmState();
+}
+
+static void sm_interactCallback(SmcConn smcConn, SmPointer clientData)
+{
+ if (smcConn != smcConnection)
+ return;
+ if (sm_waitingForInteraction)
+ ((QXcbSessionManager *) clientData)->exitEventLoop();
+}
+
+static void sm_saveYourselfPhase2Callback(SmcConn smcConn, SmPointer clientData)
+{
+ if (smcConn != smcConnection)
+ return;
+ sm_in_phase2 = true;
+ sm_performSaveYourself((QXcbSessionManager *) clientData);
+}
+
+
+void QSmSocketReceiver::socketActivated(int)
+{
+ IceProcessMessages(SmcGetIceConnection(smcConnection), 0, 0);
+}
+
+
+// QXcbSessionManager starts here
+
+QXcbSessionManager::QXcbSessionManager(const QString &id, const QString &key)
+ : QPlatformSessionManager(id, key)
+ , m_eventLoop(0)
+{
+ resetSmState();
+ char cerror[256];
+ char* myId = 0;
+ QByteArray b_id = id.toLatin1();
+ char* prevId = b_id.data();
+
+ SmcCallbacks cb;
+ cb.save_yourself.callback = sm_saveYourselfCallback;
+ cb.save_yourself.client_data = (SmPointer) this;
+ cb.die.callback = sm_dieCallback;
+ cb.die.client_data = (SmPointer) this;
+ cb.save_complete.callback = sm_saveCompleteCallback;
+ cb.save_complete.client_data = (SmPointer) this;
+ cb.shutdown_cancelled.callback = sm_shutdownCancelledCallback;
+ cb.shutdown_cancelled.client_data = (SmPointer) this;
+
+ // avoid showing a warning message below
+ if (!qEnvironmentVariableIsSet("SESSION_MANAGER"))
+ return;
+
+ smcConnection = SmcOpenConnection(0, 0, 1, 0,
+ SmcSaveYourselfProcMask |
+ SmcDieProcMask |
+ SmcSaveCompleteProcMask |
+ SmcShutdownCancelledProcMask,
+ &cb,
+ prevId,
+ &myId,
+ 256, cerror);
+
+ setSessionId(QString::fromLatin1(myId));
+ ::free(myId); // it was allocated by C
+
+ QString error = QString::fromLocal8Bit(cerror);
+ if (!smcConnection)
+ qWarning("Qt: Session management error: %s", qPrintable(error));
+ else
+ sm_receiver = new QSmSocketReceiver(IceConnectionNumber(SmcGetIceConnection(smcConnection)));
+}
+
+QXcbSessionManager::~QXcbSessionManager()
+{
+ if (smcConnection)
+ SmcCloseConnection(smcConnection, 0, 0);
+ smcConnection = 0;
+ delete sm_receiver;
+}
+
+
+void* QXcbSessionManager::handle() const
+{
+ return (void*) smcConnection;
+}
+
+void QXcbSessionManager::setSessionId(const QString &id)
+{
+ m_sessionId = id;
+}
+
+void QXcbSessionManager::setSessionKey(const QString &key)
+{
+ m_sessionKey = key;
+}
+
+bool QXcbSessionManager::allowsInteraction()
+{
+ if (sm_interactionActive)
+ return true;
+
+ if (sm_waitingForInteraction)
+ return false;
+
+ if (sm_interactStyle == SmInteractStyleAny) {
+ sm_waitingForInteraction = SmcInteractRequest(smcConnection,
+ SmDialogNormal,
+ sm_interactCallback,
+ (SmPointer*) this);
+ }
+ if (sm_waitingForInteraction) {
+ QEventLoop eventLoop;
+ m_eventLoop = &eventLoop;
+ eventLoop.exec();
+ m_eventLoop = 0;
+
+ sm_waitingForInteraction = false;
+ if (sm_smActive) { // not cancelled
+ sm_interactionActive = true;
+ qt_sm_blockUserInput = false;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool QXcbSessionManager::allowsErrorInteraction()
+{
+ if (sm_interactionActive)
+ return true;
+
+ if (sm_waitingForInteraction)
+ return false;
+
+ if (sm_interactStyle == SmInteractStyleAny || sm_interactStyle == SmInteractStyleErrors) {
+ sm_waitingForInteraction = SmcInteractRequest(smcConnection,
+ SmDialogError,
+ sm_interactCallback,
+ (SmPointer*) this);
+ }
+ if (sm_waitingForInteraction) {
+ QEventLoop eventLoop;
+ m_eventLoop = &eventLoop;
+ eventLoop.exec();
+ m_eventLoop = 0;
+
+ sm_waitingForInteraction = false;
+ if (sm_smActive) { // not cancelled
+ sm_interactionActive = true;
+ qt_sm_blockUserInput = false;
+ return true;
+ }
+ }
+ return false;
+}
+
+void QXcbSessionManager::release()
+{
+ if (sm_interactionActive) {
+ SmcInteractDone(smcConnection, False);
+ sm_interactionActive = false;
+ if (sm_smActive && sm_isshutdown)
+ qt_sm_blockUserInput = true;
+ }
+}
+
+void QXcbSessionManager::cancel()
+{
+ sm_cancel = true;
+}
+
+void QXcbSessionManager::setManagerProperty(const QString &name, const QString &value)
+{
+ sm_setProperty(name, value);
+}
+
+void QXcbSessionManager::setManagerProperty(const QString &name, const QStringList &value)
+{
+ sm_setProperty(name, value);
+}
+
+bool QXcbSessionManager::isPhase2() const
+{
+ return sm_in_phase2;
+}
+
+void QXcbSessionManager::requestPhase2()
+{
+ sm_phase2 = true;
+}
+
+void QXcbSessionManager::appCommitData()
+{
+ QPlatformSessionManager::appCommitData();
+}
+
+void QXcbSessionManager::appSaveState()
+{
+ QPlatformSessionManager::appSaveState();
+}
+
+void QXcbSessionManager::exitEventLoop()
+{
+ m_eventLoop->exit();
+}
+
+#include "qxcbsessionmanager.moc"
diff --git a/src/plugins/platforms/xcb/qxcbsessionmanager.h b/src/plugins/platforms/xcb/qxcbsessionmanager.h
new file mode 100644
index 0000000000..0dca36d16f
--- /dev/null
+++ b/src/plugins/platforms/xcb/qxcbsessionmanager.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Teo Mrnjavac <teo@kde.org>
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QXCBSESSIONMANAGER_H
+#define QXCBSESSIONMANAGER_H
+
+#include <qpa/qplatformsessionmanager.h>
+
+QT_BEGIN_NAMESPACE
+
+class QEventLoop;
+
+class QXcbSessionManager : public QPlatformSessionManager
+{
+public:
+ QXcbSessionManager(const QString &id, const QString &key);
+ virtual ~QXcbSessionManager();
+
+ void *handle() const;
+
+ void setSessionId(const QString &id);
+ void setSessionKey(const QString &key);
+
+ bool allowsInteraction() Q_DECL_OVERRIDE;
+ bool allowsErrorInteraction() Q_DECL_OVERRIDE;
+ void release() Q_DECL_OVERRIDE;
+
+ void cancel() Q_DECL_OVERRIDE;
+
+ void setManagerProperty(const QString &name, const QString &value) Q_DECL_OVERRIDE;
+ void setManagerProperty(const QString &name, const QStringList &value) Q_DECL_OVERRIDE;
+
+ bool isPhase2() const Q_DECL_OVERRIDE;
+ void requestPhase2() Q_DECL_OVERRIDE;
+
+ void appCommitData() Q_DECL_OVERRIDE;
+ void appSaveState() Q_DECL_OVERRIDE;
+
+ void exitEventLoop();
+
+private:
+ QEventLoop *m_eventLoop;
+
+ Q_DISABLE_COPY(QXcbSessionManager)
+};
+
+QT_END_NAMESPACE
+
+#endif //QXCBSESSIONMANAGER_H
diff --git a/src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp b/src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp
new file mode 100644
index 0000000000..649d450907
--- /dev/null
+++ b/src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp
@@ -0,0 +1,180 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qxcbsystemtraytracker.h"
+#include "qxcbconnection.h"
+#include "qxcbscreen.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QRect>
+#include <QtGui/QScreen>
+
+#include <qpa/qplatformnativeinterface.h>
+
+QT_BEGIN_NAMESPACE
+
+enum {
+ SystemTrayRequestDock = 0,
+ SystemTrayBeginMessage = 1,
+ SystemTrayCancelMessage = 2
+};
+
+// QXcbSystemTrayTracker provides API for accessing the tray window and tracks
+// its lifecyle by listening for its destruction and recreation.
+// See http://standards.freedesktop.org/systemtray-spec/systemtray-spec-latest.html
+
+QXcbSystemTrayTracker *QXcbSystemTrayTracker::create(QXcbConnection *connection)
+{
+ // Selection, tray atoms for GNOME, NET WM Specification
+ const xcb_atom_t trayAtom = connection->atom(QXcbAtom::_NET_SYSTEM_TRAY_OPCODE);
+ if (!trayAtom)
+ return 0;
+ const QByteArray netSysTray = QByteArrayLiteral("_NET_SYSTEM_TRAY_S") + QByteArray::number(connection->primaryScreen());
+ const xcb_atom_t selection = connection->internAtom(netSysTray.constData());
+ if (!selection)
+ return 0;
+ return new QXcbSystemTrayTracker(connection, trayAtom, selection, connection);
+}
+
+QXcbSystemTrayTracker::QXcbSystemTrayTracker(QXcbConnection *connection,
+ xcb_atom_t trayAtom,
+ xcb_atom_t selection,
+ QObject *parent)
+ : QObject(parent)
+ , m_selection(selection)
+ , m_trayAtom(trayAtom)
+ , m_connection(connection)
+ , m_trayWindow(0)
+{
+}
+
+xcb_window_t QXcbSystemTrayTracker::locateTrayWindow(const QXcbConnection *connection, xcb_atom_t selection)
+{
+ xcb_get_selection_owner_cookie_t cookie = xcb_get_selection_owner(connection->xcb_connection(), selection);
+ xcb_get_selection_owner_reply_t *reply = xcb_get_selection_owner_reply(connection->xcb_connection(), cookie, 0);
+ if (!reply)
+ return 0;
+ const xcb_window_t result = reply->owner;
+ free(reply);
+ return result;
+}
+
+// API for QPlatformNativeInterface/QPlatformSystemTrayIcon: Request a window
+// to be docked on the tray.
+void QXcbSystemTrayTracker::requestSystemTrayWindowDock(xcb_window_t window) const
+{
+ xcb_client_message_event_t trayRequest;
+ memset(&trayRequest, 0, sizeof(trayRequest));
+ trayRequest.response_type = XCB_CLIENT_MESSAGE;
+ trayRequest.format = 32;
+ trayRequest.window = m_trayWindow;
+ trayRequest.type = m_trayAtom;
+ trayRequest.data.data32[0] = XCB_CURRENT_TIME;
+ trayRequest.data.data32[1] = SystemTrayRequestDock;
+ trayRequest.data.data32[2] = window;
+ xcb_send_event(m_connection->xcb_connection(), 0, m_trayWindow, XCB_EVENT_MASK_NO_EVENT, (const char *)&trayRequest);
+}
+
+// API for QPlatformNativeInterface/QPlatformSystemTrayIcon: Return tray window.
+xcb_window_t QXcbSystemTrayTracker::trayWindow()
+{
+ if (!m_trayWindow) {
+ m_trayWindow = QXcbSystemTrayTracker::locateTrayWindow(m_connection, m_selection);
+ if (m_trayWindow) { // Listen for DestroyNotify on tray.
+ m_connection->addWindowEventListener(m_trayWindow, this);
+ const quint32 mask = XCB_CW_EVENT_MASK;
+ const quint32 value = XCB_EVENT_MASK_STRUCTURE_NOTIFY;
+ Q_XCB_CALL2(xcb_change_window_attributes(m_connection->xcb_connection(), m_trayWindow, mask, &value), m_connection);
+ }
+ }
+ return m_trayWindow;
+}
+
+// API for QPlatformNativeInterface/QPlatformSystemTrayIcon: Return the geometry of a
+// a window parented on the tray. Determines the global geometry via XCB since mapToGlobal
+// does not work for the QWindow parented on the tray.
+QRect QXcbSystemTrayTracker::systemTrayWindowGlobalGeometry(xcb_window_t window) const
+{
+ xcb_connection_t *conn = m_connection->xcb_connection();
+ xcb_get_geometry_reply_t *geomReply =
+ xcb_get_geometry_reply(conn, xcb_get_geometry(conn, window), 0);
+ if (!geomReply)
+ return QRect();
+
+ xcb_translate_coordinates_reply_t *translateReply =
+ xcb_translate_coordinates_reply(conn, xcb_translate_coordinates(conn, window, m_connection->rootWindow(), 0, 0), 0);
+ if (!translateReply) {
+ free(geomReply);
+ return QRect();
+ }
+
+ const QRect result(QPoint(translateReply->dst_x, translateReply->dst_y), QSize(geomReply->width, geomReply->height));
+ free(translateReply);
+ return result;
+}
+
+inline void QXcbSystemTrayTracker::emitSystemTrayWindowChanged()
+{
+ const int screen = m_connection->primaryScreen();
+ if (screen >= 0 && screen < m_connection->screens().size()) {
+ const QPlatformScreen *ps = m_connection->screens().at(screen);
+ emit systemTrayWindowChanged(ps->screen());
+ }
+}
+
+// Client messages with the "MANAGER" atom on the root window indicate creation of a new tray.
+void QXcbSystemTrayTracker::notifyManagerClientMessageEvent(const xcb_client_message_event_t *t)
+{
+ if (t->data.data32[1] == m_selection)
+ emitSystemTrayWindowChanged();
+}
+
+// Listen for destruction of the tray.
+void QXcbSystemTrayTracker::handleDestroyNotifyEvent(const xcb_destroy_notify_event_t *event)
+{
+ if (event->window == m_trayWindow) {
+ m_connection->removeWindowEventListener(m_trayWindow);
+ m_trayWindow = 0;
+ emitSystemTrayWindowChanged();
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/qxcbsystemtraytracker.h b/src/plugins/platforms/xcb/qxcbsystemtraytracker.h
new file mode 100644
index 0000000000..c6b0a0659e
--- /dev/null
+++ b/src/plugins/platforms/xcb/qxcbsystemtraytracker.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QXCBSYSTEMTRAYTRACKER_H
+#define QXCBSYSTEMTRAYTRACKER_H
+
+#include "qxcbconnection.h"
+
+#include <xcb/xcb.h>
+
+QT_BEGIN_NAMESPACE
+
+class QXcbConnection;
+class QScreen;
+
+class QXcbSystemTrayTracker : public QObject, public QXcbWindowEventListener
+{
+ Q_OBJECT
+public:
+ static QXcbSystemTrayTracker *create(QXcbConnection *connection);
+
+ xcb_window_t trayWindow();
+ void requestSystemTrayWindowDock(xcb_window_t window) const;
+ QRect systemTrayWindowGlobalGeometry(xcb_window_t window) const;
+
+ void notifyManagerClientMessageEvent(const xcb_client_message_event_t *);
+
+ void handleDestroyNotifyEvent(const xcb_destroy_notify_event_t *);
+
+signals:
+ void systemTrayWindowChanged(QScreen *screen);
+
+private:
+ explicit QXcbSystemTrayTracker(QXcbConnection *connection,
+ xcb_atom_t trayAtom,
+ xcb_atom_t selection,
+ QObject *parent = 0);
+ static xcb_window_t locateTrayWindow(const QXcbConnection *connection, xcb_atom_t selection);
+ void emitSystemTrayWindowChanged();
+
+ const xcb_atom_t m_selection;
+ const xcb_atom_t m_trayAtom;
+ QXcbConnection *m_connection;
+ xcb_window_t m_trayWindow;
+};
+
+QT_END_NAMESPACE
+
+#endif // QXCBSYSTEMTRAYTRACKER_H
diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp
index 028cd9ab72..46c3fdce5c 100644
--- a/src/plugins/platforms/xcb/qxcbwindow.cpp
+++ b/src/plugins/platforms/xcb/qxcbwindow.cpp
@@ -57,6 +57,8 @@
#include <qpa/qplatformintegration.h>
+#include <algorithm>
+
// FIXME This workaround can be removed for xcb-icccm > 3.8
#define class class_name
#include <xcb/xcb_icccm.h>
@@ -152,7 +154,7 @@ enum QX11EmbedMessageType {
XEMBED_ACTIVATE_ACCELERATOR = 14
};
-static unsigned int XEMBED_VERSION = 0;
+const long XEMBED_VERSION = 0;
// Returns true if we should set WM_TRANSIENT_FOR on \a w
static inline bool isTransient(const QWindow *w)
@@ -779,21 +781,21 @@ QXcbWindow::NetWmStates QXcbWindow::netWmStates()
if (reply && reply->format == 32 && reply->type == XCB_ATOM_ATOM) {
const xcb_atom_t *states = static_cast<const xcb_atom_t *>(xcb_get_property_value(reply));
const xcb_atom_t *statesEnd = states + reply->length;
- if (statesEnd != qFind(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_ABOVE)))
+ if (statesEnd != std::find(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_ABOVE)))
result |= NetWmStateAbove;
- if (statesEnd != qFind(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_BELOW)))
+ if (statesEnd != std::find(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_BELOW)))
result |= NetWmStateBelow;
- if (statesEnd != qFind(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_FULLSCREEN)))
+ if (statesEnd != std::find(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_FULLSCREEN)))
result |= NetWmStateFullScreen;
- if (statesEnd != qFind(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_HORZ)))
+ if (statesEnd != std::find(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_HORZ)))
result |= NetWmStateMaximizedHorz;
- if (statesEnd != qFind(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_VERT)))
+ if (statesEnd != std::find(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_VERT)))
result |= NetWmStateMaximizedVert;
- if (statesEnd != qFind(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_MODAL)))
+ if (statesEnd != std::find(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_MODAL)))
result |= NetWmStateModal;
- if (statesEnd != qFind(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_STAYS_ON_TOP)))
+ if (statesEnd != std::find(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_STAYS_ON_TOP)))
result |= NetWmStateStaysOnTop;
- if (statesEnd != qFind(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_DEMANDS_ATTENTION)))
+ if (statesEnd != std::find(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_DEMANDS_ATTENTION)))
result |= NetWmStateDemandsAttention;
free(reply);
} else {
@@ -1502,6 +1504,9 @@ void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *even
} else if (event->data.data32[0] == atom(QXcbAtom::WM_TAKE_FOCUS)) {
connection()->setTime(event->data.data32[1]);
} else if (event->data.data32[0] == atom(QXcbAtom::_NET_WM_PING)) {
+ if (event->window == m_screen->root())
+ return;
+
xcb_client_message_event_t reply = *event;
reply.response_type = XCB_CLIENT_MESSAGE;
@@ -1515,8 +1520,7 @@ void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *even
m_syncValue.hi = event->data.data32[3];
#ifndef QT_NO_WHATSTHIS
} else if (event->data.data32[0] == atom(QXcbAtom::_NET_WM_CONTEXT_HELP)) {
- QEvent *e = new QEvent(QEvent::EnterWhatsThisMode);
- QGuiApplication::postEvent(QGuiApplication::instance(), e);
+ QWindowSystemInterface::handleEnterWhatsThisEvent();
#endif
} else {
qWarning() << "QXcbWindow: Unhandled WM_PROTOCOLS message:" << connection()->atomName(event->data.data32[0]);
@@ -1533,6 +1537,11 @@ void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *even
#endif
} else if (event->type == atom(QXcbAtom::_XEMBED)) {
handleXEmbedMessage(event);
+ } else if (event->type == atom(QXcbAtom::MANAGER) || event->type == atom(QXcbAtom::_NET_ACTIVE_WINDOW)
+ || event->type == atom(QXcbAtom::_NET_WM_STATE) || event->type == atom(QXcbAtom::MANAGER)
+ || event->type == atom(QXcbAtom::WM_CHANGE_STATE)) {
+ // Ignore _NET_ACTIVE_WINDOW, _NET_WM_STATE, MANAGER which are relate to tray icons
+ // and other messages.
} else {
qWarning() << "QXcbWindow: Unhandled client message:" << connection()->atomName(event->type);
}
diff --git a/src/plugins/platforms/xcb/xcb-plugin.pro b/src/plugins/platforms/xcb/xcb-plugin.pro
index 82995286c4..fa1dc12eb5 100644
--- a/src/plugins/platforms/xcb/xcb-plugin.pro
+++ b/src/plugins/platforms/xcb/xcb-plugin.pro
@@ -21,7 +21,8 @@ SOURCES = \
qxcbnativeinterface.cpp \
qxcbcursor.cpp \
qxcbimage.cpp \
- qxcbxsettings.cpp
+ qxcbxsettings.cpp \
+ qxcbsystemtraytracker.cpp
HEADERS = \
qxcbclipboard.h \
@@ -38,7 +39,8 @@ HEADERS = \
qxcbnativeinterface.h \
qxcbcursor.h \
qxcbimage.h \
- qxcbxsettings.h
+ qxcbxsettings.h \
+ qxcbsystemtraytracker.h
LIBS += -ldl
@@ -70,6 +72,13 @@ contains(QT_CONFIG, xcb-render) {
LIBS += -lxcb-render -lxcb-render-util -lXrender
}
+# build with session management support
+contains(QT_CONFIG, xcb-sm) {
+ LIBS += -lSM -lICE
+ SOURCES += qxcbsessionmanager.cpp
+ HEADERS += qxcbsessionmanager.h
+}
+
contains(QT_CONFIG, opengl) {
contains(QT_CONFIG, opengles2) {
DEFINES += XCB_USE_EGL