diff options
Diffstat (limited to 'src/plugins/platforms/xcb')
-rw-r--r-- | src/plugins/platforms/xcb/main.cpp | 8 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbconnection.cpp | 33 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbconnection.h | 15 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbconnection_xi2.cpp | 290 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbintegration.cpp | 41 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbintegration.h | 7 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbkeyboard.cpp | 8 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbnativeinterface.cpp | 109 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbnativeinterface.h | 13 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbscreen.cpp | 3 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbsessionmanager.cpp | 512 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbsessionmanager.h | 88 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp | 180 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbsystemtraytracker.h | 87 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbwindow.cpp | 31 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/xcb-plugin.pro | 13 |
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 ¶meters) +QXcbIntegration::QXcbIntegration(const QStringList ¶meters, 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 ¶meters) 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 ¶meters); + QXcbIntegration(const QStringList ¶meters, 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] = ∝ + 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 |