123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634 | // Copyright (C) 2016 The Qt Company Ltd.// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only#include"qxcbintegration.h"#include"qxcbconnection.h"#include"qxcbscreen.h"#include"qxcbwindow.h"#include"qxcbcursor.h"#include"qxcbkeyboard.h"#include"qxcbbackingstore.h"#include"qxcbnativeinterface.h"#include"qxcbclipboard.h"#include"qxcbeventqueue.h"#include"qxcbeventdispatcher.h"#if QT_CONFIG(draganddrop)#include"qxcbdrag.h"#endif#include"qxcbglintegration.h"#ifndef QT_NO_SESSIONMANAGER#include"qxcbsessionmanager.h"#endif#include"qxcbxsettings.h"#include <xcb/xcb.h>#include <QtGui/private/qgenericunixfontdatabase_p.h>#include <QtGui/private/qdesktopunixservices_p.h>#include <stdio.h>#include <QtGui/private/qguiapplication_p.h>#if QT_CONFIG(xcb_xlib)#define register/* C++17 deprecated register */#include <X11/Xlib.h>#undef register#endif#if QT_CONFIG(xcb_native_painting)#include"qxcbnativepainting.h"#include"qpixmap_x11_p.h"#include"qbackingstore_x11_p.h"#endif#include <qpa/qplatforminputcontextfactory_p.h>#include <private/qgenericunixtheme_p.h>#if QT_CONFIG(dbus)#include <private/qkdetheme_p.h>#endif#include <qpa/qplatforminputcontext.h>#include <QtGui/QOpenGLContext>#include <QtGui/QScreen>#include <QtGui/QOffscreenSurface>#if QT_CONFIG(accessibility)#include <qpa/qplatformaccessibility.h>#if QT_CONFIG(accessibility_atspi_bridge)#include <QtGui/private/qspiaccessiblebridge_p.h>#endif#endif#include <QtCore/QFileInfo>#if QT_CONFIG(vulkan)#include"qxcbvulkaninstance.h"#include"qxcbvulkanwindow.h"#endif QT_BEGIN_NAMESPACE using namespaceQt::StringLiterals;// Find out if our parent process is gdb by looking at the 'exe' symlink under /proc,.// or, for older Linuxes, read out 'cmdline'.static boolrunningUnderDebugger(){#if defined(Q_OS_LINUX)const QString parentProc ="/proc/"_L1 +QString::number(getppid());const QFileInfo parentProcExe(parentProc +"/exe"_L1);if(parentProcExe.isSymLink())return parentProcExe.symLinkTarget().endsWith("/gdb"_L1); QFile f(parentProc +"/cmdline"_L1);if(!f.open(QIODevice::ReadOnly))return false; QByteArray s;char c;while(f.getChar(&c) && c) {if(c =='/') s.clear();else s += c;}return s =="gdb";#elsereturn false;#endif}class QXcbUnixServices :public QDesktopUnixServices,public QXcbObject {public: QString portalWindowIdentifier(QWindow *window) override;voidregisterDBusMenuForWindow(QWindow *window,const QString &service,const QString &path) override;voidunregisterDBusMenuForWindow(QWindow *window) override;}; QXcbIntegration *QXcbIntegration::m_instance =nullptr;QXcbIntegration::QXcbIntegration(const QStringList ¶meters,int&argc,char**argv):m_services(new QXcbUnixServices),m_instanceName(nullptr),m_canGrab(true),m_defaultVisualId(UINT_MAX){Q_UNUSED(parameters); m_instance =this; qApp->setAttribute(Qt::AA_CompressHighFrequencyEvents,true); qRegisterMetaType<QXcbWindow*>();#if QT_CONFIG(xcb_xlib)XInitThreads();#endif m_nativeInterface.reset(new QXcbNativeInterface);// Parse argumentsconst char*displayName =nullptr;bool noGrabArg =false;bool doGrabArg =false;if(argc) {int j =1;for(int i =1; i < argc; i++) { QByteArray arg(argv[i]);if(arg.startsWith("--")) arg.remove(0,1);if(arg =="-display"&& i < argc -1) displayName = argv[++i];else if(arg =="-name"&& i < argc -1) m_instanceName = argv[++i];else if(arg =="-nograb") noGrabArg =true;else if(arg =="-dograb") doGrabArg =true;else if(arg =="-visual"&& i < argc -1) {bool ok =false; m_defaultVisualId =QByteArray(argv[++i]).toUInt(&ok,0);if(!ok) m_defaultVisualId = UINT_MAX;}else argv[j++] = argv[i];} argc = j;}// argcbool underDebugger =runningUnderDebugger();if(noGrabArg && doGrabArg && underDebugger) {qWarning("Both -nograb and -dograb command line arguments specified. Please pick one. -nograb takes precedence"); doGrabArg =false;}if(!noGrabArg && !doGrabArg && underDebugger) {qCDebug(lcQpaXcb,"Qt: gdb: -nograb added to command-line options.\n""\tUse the -dograb option to enforce grabbing.");} m_canGrab = (!underDebugger && !noGrabArg) || (underDebugger && doGrabArg);static bool canNotGrabEnv =qEnvironmentVariableIsSet("QT_XCB_NO_GRAB_SERVER");if(canNotGrabEnv) m_canGrab =false; m_connection =newQXcbConnection(m_nativeInterface.data(), m_canGrab, m_defaultVisualId, displayName);if(!m_connection->isConnected()) {delete m_connection; m_connection =nullptr;return;} m_services->setConnection(m_connection); m_fontDatabase.reset(newQGenericUnixFontDatabase());#if QT_CONFIG(xcb_native_painting)if(nativePaintingEnabled()) {qCDebug(lcQpaXcb,"QXCB USING NATIVE PAINTING");qt_xcb_native_x11_info_init(connection());}#endif}QXcbIntegration::~QXcbIntegration(){delete m_connection; m_connection =nullptr; m_instance =nullptr;} QPlatformPixmap *QXcbIntegration::createPlatformPixmap(QPlatformPixmap::PixelType type)const{#if QT_CONFIG(xcb_native_painting)if(nativePaintingEnabled())return newQX11PlatformPixmap(type);#endifreturnQPlatformIntegration::createPlatformPixmap(type);} QPlatformWindow *QXcbIntegration::createPlatformWindow(QWindow *window)const{ QXcbGlIntegration *glIntegration =nullptr;const bool isTrayIconWindow =QXcbWindow::isTrayIconWindow(window);if(window->type() !=Qt::Desktop && !isTrayIconWindow) {if(window->supportsOpenGL()) { glIntegration =connection()->glIntegration();if(glIntegration) { QXcbWindow *xcbWindow = glIntegration->createWindow(window); xcbWindow->create();return xcbWindow;}#if QT_CONFIG(vulkan)}else if(window->surfaceType() ==QSurface::VulkanSurface) { QXcbWindow *xcbWindow =newQXcbVulkanWindow(window); xcbWindow->create();return xcbWindow;#endif}}Q_ASSERT(window->type() ==Qt::Desktop || isTrayIconWindow || !window->supportsOpenGL()|| (!glIntegration && window->surfaceType() ==QSurface::RasterGLSurface));// for VNC QXcbWindow *xcbWindow =newQXcbWindow(window); xcbWindow->create();return xcbWindow;} QPlatformWindow *QXcbIntegration::createForeignWindow(QWindow *window, WId nativeHandle)const{return newQXcbForeignWindow(window, nativeHandle);}#ifndef QT_NO_OPENGL QPlatformOpenGLContext *QXcbIntegration::createPlatformOpenGLContext(QOpenGLContext *context)const{ QXcbGlIntegration *glIntegration = m_connection->glIntegration();if(!glIntegration) {qWarning("QXcbIntegration: Cannot create platform OpenGL context, neither GLX nor EGL are enabled");returnnullptr;}return glIntegration->createPlatformOpenGLContext(context);}# if QT_CONFIG(xcb_glx_plugin) QOpenGLContext *QXcbIntegration::createOpenGLContext(GLXContext context,void*visualInfo, QOpenGLContext *shareContext)const{using namespaceQNativeInterface::Private;if(auto*glxIntegration =dynamic_cast<QGLXIntegration*>(m_connection->glIntegration()))return glxIntegration->createOpenGLContext(context, visualInfo, shareContext);elsereturnnullptr;}# endif#if QT_CONFIG(egl) QOpenGLContext *QXcbIntegration::createOpenGLContext(EGLContext context, EGLDisplay display, QOpenGLContext *shareContext)const{using namespaceQNativeInterface::Private;if(auto*eglIntegration =dynamic_cast<QEGLIntegration*>(m_connection->glIntegration()))return eglIntegration->createOpenGLContext(context, display, shareContext);elsereturnnullptr;}#endif#endif// QT_NO_OPENGL QPlatformBackingStore *QXcbIntegration::createPlatformBackingStore(QWindow *window)const{ QPlatformBackingStore *backingStore =nullptr;const bool isTrayIconWindow =QXcbWindow::isTrayIconWindow(window);if(isTrayIconWindow) { backingStore =newQXcbSystemTrayBackingStore(window);#if QT_CONFIG(xcb_native_painting)}else if(nativePaintingEnabled()) { backingStore =newQXcbNativeBackingStore(window);#endif}else{ backingStore =newQXcbBackingStore(window);}Q_ASSERT(backingStore);return backingStore;} QPlatformOffscreenSurface *QXcbIntegration::createPlatformOffscreenSurface(QOffscreenSurface *surface)const{ QXcbScreen *screen =static_cast<QXcbScreen *>(surface->screen()->handle()); QXcbGlIntegration *glIntegration = screen->connection()->glIntegration();if(!glIntegration) {qWarning("QXcbIntegration: Cannot create platform offscreen surface, neither GLX nor EGL are enabled");returnnullptr;}return glIntegration->createPlatformOffscreenSurface(surface);}boolQXcbIntegration::hasCapability(QPlatformIntegration::Capability cap)const{switch(cap) {case OpenGL:case ThreadedOpenGL:{if(constauto*integration =connection()->glIntegration())return cap != ThreadedOpenGL || integration->supportsThreadedOpenGL();return false;}case ThreadedPixmaps:case WindowMasks:case MultipleWindows:case ForeignWindows:case SyncState:case RasterGLSurface:return true;case SwitchableWidgetComposition:{return m_connection->glIntegration()&& m_connection->glIntegration()->supportsSwitchableWidgetComposition();}default:returnQPlatformIntegration::hasCapability(cap);}} QAbstractEventDispatcher *QXcbIntegration::createEventDispatcher()const{returnQXcbEventDispatcher::createEventDispatcher(connection());}using namespaceQt::Literals::StringLiterals;static constauto xsNetCursorBlink ="Net/CursorBlink"_ba;static constauto xsNetCursorBlinkTime ="Net/CursorBlinkTime"_ba;static constauto xsNetDoubleClickTime ="Net/DoubleClickTime"_ba;static constauto xsNetDoubleClickDistance ="Net/DoubleClickDistance"_ba;static constauto xsNetDndDragThreshold ="Net/DndDragThreshold"_ba;voidQXcbIntegration::initialize(){constauto defaultInputContext ="compose"_L1;// Perform everything that may potentially need the event dispatcher (timers, socket// notifiers) here instead of the constructor.auto icStrs =QPlatformInputContextFactory::requested();if(icStrs.isEmpty()) icStrs = { defaultInputContext }; m_inputContext.reset(QPlatformInputContextFactory::create(icStrs));if(!m_inputContext && !icStrs.contains(defaultInputContext)&& icStrs != QStringList{"none"_L1}) m_inputContext.reset(QPlatformInputContextFactory::create(defaultInputContext));connection()->keyboard()->initialize();auto notifyThemeChanged = [](QXcbVirtualDesktop *,const QByteArray &,const QVariant &,void*) {QWindowSystemInterface::handleThemeChange();};auto*xsettings =connection()->primaryScreen()->xSettings(); xsettings->registerCallbackForProperty(xsNetCursorBlink, notifyThemeChanged,this); xsettings->registerCallbackForProperty(xsNetCursorBlinkTime, notifyThemeChanged,this); xsettings->registerCallbackForProperty(xsNetDoubleClickTime, notifyThemeChanged,this); xsettings->registerCallbackForProperty(xsNetDoubleClickDistance, notifyThemeChanged,this); xsettings->registerCallbackForProperty(xsNetDndDragThreshold, notifyThemeChanged,this);}voidQXcbIntegration::moveToScreen(QWindow *window,int screen){Q_UNUSED(window);Q_UNUSED(screen);} QPlatformFontDatabase *QXcbIntegration::fontDatabase()const{return m_fontDatabase.data();} QPlatformNativeInterface *QXcbIntegration::nativeInterface()const{return m_nativeInterface.data();}#ifndef QT_NO_CLIPBOARD QPlatformClipboard *QXcbIntegration::clipboard()const{return m_connection->clipboard();}#endif#if QT_CONFIG(draganddrop)#include <private/qsimpledrag_p.h> QPlatformDrag *QXcbIntegration::drag()const{static const bool useSimpleDrag =qEnvironmentVariableIsSet("QT_XCB_USE_SIMPLE_DRAG");if(Q_UNLIKELY(useSimpleDrag)) {// This is useful for testing purposesstatic QSimpleDrag *simpleDrag =nullptr;if(!simpleDrag) simpleDrag =newQSimpleDrag();return simpleDrag;}return m_connection->drag();}#endif QPlatformInputContext *QXcbIntegration::inputContext()const{return m_inputContext.data();}#if QT_CONFIG(accessibility) QPlatformAccessibility *QXcbIntegration::accessibility()const{#if !defined(QT_NO_ACCESSIBILITY_ATSPI_BRIDGE)if(!m_accessibility) {Q_ASSERT_X(QCoreApplication::eventDispatcher(),"QXcbIntegration","Initializing accessibility without event-dispatcher!"); m_accessibility.reset(newQSpiAccessibleBridge());}#endifreturn m_accessibility.data();}#endif QPlatformServices *QXcbIntegration::services()const{return m_services.data();} QPlatformKeyMapper *QXcbIntegration::keyMapper()const{return m_connection->keyboard();} QStringList QXcbIntegration::themeNames()const{returnQGenericUnixTheme::themeNames();} QPlatformTheme *QXcbIntegration::createPlatformTheme(const QString &name)const{returnQGenericUnixTheme::createUnixTheme(name);}#define RETURN_VALID_XSETTINGS(key) { \ auto value = connection()->primaryScreen()->xSettings()->setting(key); \ if (value.isValid()) return value; \} QVariant QXcbIntegration::styleHint(QPlatformIntegration::StyleHint hint)const{switch(hint) {caseQPlatformIntegration::CursorFlashTime: {bool ok =false;// If cursor blinking is off, returns 0 to keep the cursor awlays display.if(connection()->primaryScreen()->xSettings()->setting(xsNetCursorBlink).toInt(&ok) ==0&& ok)return0;RETURN_VALID_XSETTINGS(xsNetCursorBlinkTime);break;}caseQPlatformIntegration::MouseDoubleClickInterval:RETURN_VALID_XSETTINGS(xsNetDoubleClickTime);break;caseQPlatformIntegration::MouseDoubleClickDistance:RETURN_VALID_XSETTINGS(xsNetDoubleClickDistance);break;caseQPlatformIntegration::KeyboardInputInterval:caseQPlatformIntegration::StartDragTime:caseQPlatformIntegration::KeyboardAutoRepeatRate:caseQPlatformIntegration::PasswordMaskDelay:caseQPlatformIntegration::StartDragVelocity:caseQPlatformIntegration::UseRtlExtensions:caseQPlatformIntegration::PasswordMaskCharacter:caseQPlatformIntegration::FlickMaximumVelocity:caseQPlatformIntegration::FlickDeceleration:// TODO using various xcb, gnome or KDE settingsbreak;// Not implemented, use defaultscaseQPlatformIntegration::FlickStartDistance:caseQPlatformIntegration::StartDragDistance: {RETURN_VALID_XSETTINGS(xsNetDndDragThreshold);// The default (in QPlatformTheme::defaultThemeHint) is 10 pixels, but// on a high-resolution screen it makes sense to increase it. qreal dpi =100;if(const QXcbScreen *screen =connection()->primaryScreen()) {if(screen->logicalDpi().first > dpi) dpi = screen->logicalDpi().first;if(screen->logicalDpi().second > dpi) dpi = screen->logicalDpi().second;}return(hint ==QPlatformIntegration::FlickStartDistance ?qreal(15) :qreal(10)) * dpi /qreal(100);}caseQPlatformIntegration::ShowIsFullScreen:// X11 always has support for windows, but the// window manager could prevent it (e.g. matchbox)return false;caseQPlatformIntegration::ReplayMousePressOutsidePopup:return false;default:break;}returnQPlatformIntegration::styleHint(hint);}static QString argv0BaseName(){ QString result;const QStringList arguments =QCoreApplication::arguments();if(!arguments.isEmpty() && !arguments.front().isEmpty()) { result = arguments.front();const int lastSlashPos = result.lastIndexOf(u'/');if(lastSlashPos != -1) result.remove(0, lastSlashPos +1);}return result;}static const char resourceNameVar[] ="RESOURCE_NAME"; 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 =qEnvironmentVariable(resourceNameVar);if(name.isEmpty()) name =argv0BaseName();// Note: QCoreApplication::applicationName() cannot be called from the QGuiApplication constructor,// hence this delayed initialization. QString className =QCoreApplication::applicationName();if(className.isEmpty()) { className =argv0BaseName();if(!className.isEmpty() && className.at(0).isLower()) className[0] = className.at(0).toUpper();}if(!name.isEmpty() && !className.isEmpty()) m_wmClass =std::move(name).toLocal8Bit() +'\0'+std::move(className).toLocal8Bit() +'\0';}return m_wmClass;}#if QT_CONFIG(xcb_sm) QPlatformSessionManager *QXcbIntegration::createPlatformSessionManager(const QString &id,const QString &key)const{return newQXcbSessionManager(id, key);}#endifvoidQXcbIntegration::sync(){ m_connection->sync();}// For QApplication::beep()voidQXcbIntegration::beep()const{ QScreen *priScreen =QGuiApplication::primaryScreen();if(!priScreen)return; QPlatformScreen *screen = priScreen->handle();if(!screen)return; xcb_connection_t *connection =static_cast<QXcbScreen *>(screen)->xcb_connection();xcb_bell(connection,0);xcb_flush(connection);}boolQXcbIntegration::nativePaintingEnabled()const{#if QT_CONFIG(xcb_native_painting)static bool enabled =qEnvironmentVariableIsSet("QT_XCB_NATIVE_PAINTING");return enabled;#elsereturn false;#endif}#if QT_CONFIG(vulkan) QPlatformVulkanInstance *QXcbIntegration::createPlatformVulkanInstance(QVulkanInstance *instance)const{return newQXcbVulkanInstance(instance);}#endifvoidQXcbIntegration::setApplicationBadge(qint64 number){auto unixServices =dynamic_cast<QDesktopUnixServices *>(services()); unixServices->setApplicationBadge(number);} QString QXcbUnixServices::portalWindowIdentifier(QWindow *window){return"x11:"_L1 +QString::number(window->winId(),16);}voidQXcbUnixServices::registerDBusMenuForWindow(QWindow *window,const QString &service,const QString &path){const QByteArray serviceValue = service.toLatin1();const QByteArray pathValue = path.toLatin1();xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, window->winId(),atom(QXcbAtom::Atom_KDE_NET_WM_APPMENU_SERVICE_NAME), XCB_ATOM_STRING,8, serviceValue.length(), serviceValue.constData());xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, window->winId(),atom(QXcbAtom::Atom_KDE_NET_WM_APPMENU_OBJECT_PATH), XCB_ATOM_STRING,8, pathValue.length(), pathValue.constData());}voidQXcbUnixServices::unregisterDBusMenuForWindow(QWindow *window){xcb_delete_property(xcb_connection(), window->winId(),atom(QXcbAtom::Atom_KDE_NET_WM_APPMENU_SERVICE_NAME));xcb_delete_property(xcb_connection(), window->winId(),atom(QXcbAtom::Atom_KDE_NET_WM_APPMENU_OBJECT_PATH));} QT_END_NAMESPACE
|