summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/xcb/qxcbwindow.cpp
blob: f235edea63f6b6cdb2138f17fecc2fc9eac41a39 (plain)
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705
// 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"qxcbwindow.h"#include <QtDebug>#include <QMetaEnum>#include <QScreen>#include <QtCore/QFileInfo>#include <QtGui/QIcon>#include <QtGui/QRegion>#include <QtGui/private/qhighdpiscaling_p.h>#include"qxcbintegration.h"#include"qxcbconnection.h"#include"qxcbscreen.h"#if QT_CONFIG(draganddrop)#include"qxcbdrag.h"#endif#include"qxcbkeyboard.h"#include"qxcbimage.h"#include"qxcbwmsupport.h"#include"qxcbimage.h"#include"qxcbnativeinterface.h"#include"qxcbsystemtraytracker.h"#include <qpa/qplatformintegration.h>#include <qpa/qplatformcursor.h>#include <algorithm>#include <xcb/xcb_icccm.h>#include <xcb/xfixes.h>#include <xcb/shape.h>#include <xcb/xinput.h>#include <private/qguiapplication_p.h>#include <private/qwindow_p.h>#include <qpa/qplatformbackingstore.h>#include <qpa/qwindowsysteminterface.h>#include <stdio.h>#include <unistd.h>#if QT_CONFIG(xcb_xlib)#define register/* C++17 deprecated register */#include <X11/Xlib.h>#include <X11/Xutil.h>#undef register#endif#define XCOORD_MAX 32767enum{ defaultWindowWidth =160, defaultWindowHeight =160}; QT_BEGIN_NAMESPACE using namespaceQt::StringLiterals;Q_STATIC_LOGGING_CATEGORY(lcQpaWindow,"qt.qpa.window");Q_STATIC_LOGGING_CATEGORY(lcQpaXcbWindow,"qt.qpa.xcb.window");Q_DECLARE_TYPEINFO(xcb_rectangle_t, Q_PRIMITIVE_TYPE);#undef FocusInenum QX11EmbedFocusInDetail { XEMBED_FOCUS_CURRENT =0, XEMBED_FOCUS_FIRST =1, XEMBED_FOCUS_LAST =2};enum QX11EmbedInfoFlags { XEMBED_MAPPED = (1<<0),};enum QX11EmbedMessageType { XEMBED_EMBEDDED_NOTIFY =0, XEMBED_WINDOW_ACTIVATE =1, XEMBED_WINDOW_DEACTIVATE =2, XEMBED_REQUEST_FOCUS =3, XEMBED_FOCUS_IN =4, XEMBED_FOCUS_OUT =5, XEMBED_FOCUS_NEXT =6, XEMBED_FOCUS_PREV =7, XEMBED_MODALITY_ON =10, XEMBED_MODALITY_OFF =11, XEMBED_REGISTER_ACCELERATOR =12, XEMBED_UNREGISTER_ACCELERATOR =13, XEMBED_ACTIVATE_ACCELERATOR =14};const quint32 XEMBED_VERSION =0; QXcbScreen *QXcbWindow::parentScreen(){returnQPlatformWindow::parent() ?static_cast<QXcbWindow*>(QPlatformWindow::parent())->parentScreen() :xcbScreen();} QXcbScreen *QXcbWindow::initialScreen()const{// Resolve initial screen via QWindowPrivate::screenForGeometry(),// which works in platform independent coordinates, as opposed to// QPlatformWindow::screenForGeometry() that uses native coordinates. QWindowPrivate *windowPrivate =qt_window_private(window()); QScreen *screen = windowPrivate->screenForGeometry(window()->geometry());return static_cast<QXcbScreen*>(screen->handle());}// Returns \c true if we should set WM_TRANSIENT_FOR on \a wstaticinlineboolisTransient(const QWindow *w){return w->type() ==Qt::Dialog || w->type() ==Qt::Sheet || w->type() ==Qt::Tool || w->type() ==Qt::SplashScreen || w->type() ==Qt::ToolTip || w->type() ==Qt::Drawer || w->type() ==Qt::Popup;}voidQXcbWindow::setImageFormatForVisual(const xcb_visualtype_t *visual){if(qt_xcb_imageFormatForVisual(connection(), m_depth, visual, &m_imageFormat, &m_imageRgbSwap))return;switch(m_depth) {case32:case24:qWarning("Using RGB32 fallback, if this works your X11 server is reporting a bad screen format."); m_imageFormat =QImage::Format_RGB32;break;case16:qWarning("Using RGB16 fallback, if this works your X11 server is reporting a bad screen format."); m_imageFormat =QImage::Format_RGB16;break;default:break;}}#if QT_CONFIG(xcb_xlib)staticinline XTextProperty*qstringToXTP(Display *dpy,const QString& s){#include <X11/Xatom.h>static XTextProperty tp = {nullptr,0,0,0};static bool free_prop =true;// we can't free tp.value in case it references// the data of the static QByteArray below.if(tp.value) {if(free_prop)XFree(tp.value); tp.value =nullptr; free_prop =true;}int errCode =0; QByteArray mapped = s.toLocal8Bit();// should always be utf-8char* tl[2]; tl[0] = mapped.data(); tl[1] =nullptr; errCode =XmbTextListToTextProperty(dpy, tl,1, XStdICCTextStyle, &tp);if(errCode <0)qCDebug(lcQpaXcb,"XmbTextListToTextProperty result code %d", errCode);if(errCode <0) {static QByteArray qcs; qcs = s.toLatin1(); tp.value = (uchar*)qcs.data(); tp.encoding = XA_STRING; tp.format =8; tp.nitems = qcs.size(); free_prop =false;}return&tp;}#endif// QT_CONFIG(xcb_xlib)// TODO move this into a utility function in QWindow or QGuiApplicationstatic QWindow *childWindowAt(QWindow *win,const QPoint &p){for(QObject *obj : win->children()) {if(obj->isWindowType()) { QWindow *childWin =static_cast<QWindow *>(obj);if(childWin->isVisible()) {if(QWindow *recurse =childWindowAt(childWin, p))return recurse;}}}if(!win->isTopLevel()&& !(win->flags() &Qt::WindowTransparentForInput)&& win->geometry().contains(win->parent()->mapFromGlobal(p))) {return win;}returnnullptr;}static const char*wm_window_type_property_id ="_q_xcb_wm_window_type";static const char*wm_window_role_property_id ="_q_xcb_wm_window_role";QXcbWindow::QXcbWindow(QWindow *window):QPlatformWindow(window){setConnection(xcbScreen()->connection());}enum: quint32 { baseEventMask = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_FOCUS_CHANGE, defaultEventMask = baseEventMask | XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE | XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_BUTTON_MOTION | XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW | XCB_EVENT_MASK_POINTER_MOTION, transparentForInputEventMask = baseEventMask | XCB_EVENT_MASK_VISIBILITY_CHANGE | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_COLOR_MAP_CHANGE | XCB_EVENT_MASK_OWNER_GRAB_BUTTON };voidQXcbWindow::create(){ xcb_window_t old_m_window = m_window;destroy(); m_windowState =Qt::WindowNoState; m_trayIconWindow =isTrayIconWindow(window());Qt::WindowType type =window()->type(); QXcbScreen *currentScreen =xcbScreen(); QXcbScreen *platformScreen =QPlatformWindow::parent() ?parentScreen() :initialScreen(); QRect rect =QPlatformWindow::parent()?QHighDpi::toNativeLocalPosition(window()->geometry(), platformScreen):QHighDpi::toNativePixels(window()->geometry(), platformScreen);if(type ==Qt::Desktop) { m_window = platformScreen->root(); m_depth = platformScreen->screen()->root_depth; m_visualId = platformScreen->screen()->root_visual;const xcb_visualtype_t *visual =nullptr;if(connection()->hasDefaultVisualId()) { visual = platformScreen->visualForId(connection()->defaultVisualId());if(visual) m_visualId =connection()->defaultVisualId();if(!visual)qWarning("Could not use default visual id. Falling back to root_visual for screen.");}if(!visual) visual = platformScreen->visualForId(m_visualId);setImageFormatForVisual(visual);connection()->addWindowEventListener(m_window,this);return;}const QSize minimumSize =windowMinimumSize();if(rect.width() >0|| rect.height() >0) { rect.setWidth(qBound(1, rect.width(), XCOORD_MAX)); rect.setHeight(qBound(1, rect.height(), XCOORD_MAX));}else if(minimumSize.width() >0|| minimumSize.height() >0) { rect.setSize(minimumSize);}else{ rect.setWidth(QHighDpi::toNativePixels(int(defaultWindowWidth), platformScreen->QPlatformScreen::screen())); rect.setHeight(QHighDpi::toNativePixels(int(defaultWindowHeight), platformScreen->QPlatformScreen::screen()));}QPlatformWindow::setGeometry(rect);if(platformScreen != currentScreen)QWindowSystemInterface::handleWindowScreenChanged(window(), platformScreen->QPlatformScreen::screen()); xcb_window_t xcb_parent_id = platformScreen->root();if(QPlatformWindow::parent()) { xcb_parent_id =static_cast<QXcbWindow *>(QPlatformWindow::parent())->xcb_window(); m_embedded =QPlatformWindow::parent()->isForeignWindow(); QSurfaceFormat parentFormat =QPlatformWindow::parent()->window()->requestedFormat();if(window()->surfaceType() !=QSurface::OpenGLSurface && parentFormat.hasAlpha()) {window()->setFormat(parentFormat);}}resolveFormat(platformScreen->surfaceFormatFor(window()->requestedFormat()));const xcb_visualtype_t *visual =nullptr;if(m_trayIconWindow &&connection()->systemTrayTracker()) { visual = platformScreen->visualForId(connection()->systemTrayTracker()->visualId());}else if(connection()->hasDefaultVisualId()) { visual = platformScreen->visualForId(connection()->defaultVisualId());if(!visual)qWarning() <<"Failed to use requested visual id.";}if(QPlatformWindow::parent()) {// When using a Vulkan QWindow via QWidget::createWindowContainer() we// must make sure the visuals are compatible. Now, the parent will be// of RasterGLSurface which typically chooses a GLX/EGL compatible// visual which may not be what the Vulkan window would choose.// Therefore, take the parent's visual.if(window()->surfaceType() ==QSurface::VulkanSurface &&QPlatformWindow::parent()->window()->surfaceType() !=QSurface::VulkanSurface){ visual = platformScreen->visualForId(static_cast<QXcbWindow *>(QPlatformWindow::parent())->visualId());}}if(!visual) visual =createVisual();if(!visual) {qWarning() <<"Falling back to using screens root_visual."; visual = platformScreen->visualForId(platformScreen->screen()->root_visual);}Q_ASSERT(visual); m_visualId = visual->visual_id; m_depth = platformScreen->depthOfVisual(m_visualId);setImageFormatForVisual(visual); quint32 mask = XCB_CW_BACK_PIXMAP | XCB_CW_BORDER_PIXEL | XCB_CW_BIT_GRAVITY | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_SAVE_UNDER | XCB_CW_EVENT_MASK | XCB_CW_COLORMAP; quint32 values[] = { XCB_BACK_PIXMAP_NONE, platformScreen->screen()->black_pixel, XCB_GRAVITY_NORTH_WEST, type ==Qt::Popup || type ==Qt::ToolTip || (window()->flags() &Qt::BypassWindowManagerHint), type ==Qt::Popup || type ==Qt::Tool || type ==Qt::SplashScreen || type ==Qt::ToolTip || type ==Qt::Drawer, defaultEventMask, platformScreen->colormapForVisual(m_visualId)}; m_window =xcb_generate_id(xcb_connection());xcb_create_window(xcb_connection(), m_depth, m_window,// window id xcb_parent_id,// parent window id rect.x(), rect.y(), rect.width(), rect.height(),0,// border width XCB_WINDOW_CLASS_INPUT_OUTPUT,// window class m_visualId,// visual mask, values);connection()->addWindowEventListener(m_window,this);propagateSizeHints(); xcb_atom_t properties[5];int propertyCount =0; properties[propertyCount++] =atom(QXcbAtom::AtomWM_DELETE_WINDOW); properties[propertyCount++] =atom(QXcbAtom::AtomWM_TAKE_FOCUS); properties[propertyCount++] =atom(QXcbAtom::Atom_NET_WM_PING);if(connection()->hasXSync()) properties[propertyCount++] =atom(QXcbAtom::Atom_NET_WM_SYNC_REQUEST);if(window()->flags() &Qt::WindowContextHelpButtonHint) properties[propertyCount++] =atom(QXcbAtom::Atom_NET_WM_CONTEXT_HELP);xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,atom(QXcbAtom::AtomWM_PROTOCOLS), XCB_ATOM_ATOM,32, propertyCount, properties); m_syncValue.hi =0; m_syncValue.lo =0;const QByteArray wmClass =QXcbIntegration::instance()->wmClass();if(!wmClass.isEmpty()) {xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,atom(QXcbAtom::AtomWM_CLASS), XCB_ATOM_STRING,8, wmClass.size(), wmClass.constData());} QString desktopFileName =QGuiApplication::desktopFileName();if(QGuiApplication::desktopFileName().isEmpty()) { QFileInfo fi =QFileInfo(QCoreApplication::instance()->applicationFilePath()); QStringList domainName =QCoreApplication::instance()->organizationDomain().split(QLatin1Char('.'),Qt::SkipEmptyParts);if(domainName.isEmpty()) { desktopFileName = fi.baseName();}else{for(int i =0; i < domainName.size(); ++i) desktopFileName.prepend(QLatin1Char('.')).prepend(domainName.at(i)); desktopFileName.append(fi.baseName());}}if(!desktopFileName.isEmpty()) {const QByteArray dfName = desktopFileName.toUtf8();xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,atom(QXcbAtom::Atom_KDE_NET_WM_DESKTOP_FILE),atom(QXcbAtom::AtomUTF8_STRING),8, dfName.size(), dfName.constData());xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,atom(QXcbAtom::Atom_GTK_APPLICATION_ID),atom(QXcbAtom::AtomUTF8_STRING),8, dfName.size(), dfName.constData());}if(connection()->hasXSync()) { m_syncCounter =xcb_generate_id(xcb_connection());xcb_sync_create_counter(xcb_connection(), m_syncCounter, m_syncValue);xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,atom(QXcbAtom::Atom_NET_WM_SYNC_REQUEST_COUNTER), XCB_ATOM_CARDINAL,32,1,&m_syncCounter);}// set the PID to let the WM kill the application if unresponsive quint32 pid =getpid();xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,atom(QXcbAtom::Atom_NET_WM_PID), XCB_ATOM_CARDINAL,32,1, &pid);const QByteArray clientMachine =QSysInfo::machineHostName().toLocal8Bit();if(!clientMachine.isEmpty()) {xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,atom(QXcbAtom::AtomWM_CLIENT_MACHINE), XCB_ATOM_STRING,8, clientMachine.size(), clientMachine.constData());}// Create WM_HINTS property on the window, so we can xcb_icccm_get_wm_hints*()// from various setter functions for adjusting the hints. xcb_icccm_wm_hints_t hints;memset(&hints,0,sizeof(hints)); hints.flags = XCB_ICCCM_WM_HINT_WINDOW_GROUP; hints.window_group =connection()->clientLeader();xcb_icccm_set_wm_hints(xcb_connection(), m_window, &hints); xcb_window_t leader =connection()->clientLeader();xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,atom(QXcbAtom::AtomWM_CLIENT_LEADER), XCB_ATOM_WINDOW,32,1, &leader);/* Add XEMBED info; this operation doesn't initiate the embedding. */ quint32 data[] = { XEMBED_VERSION, XEMBED_MAPPED };xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,atom(QXcbAtom::Atom_XEMBED_INFO),atom(QXcbAtom::Atom_XEMBED_INFO),32,2, (void*)data);if(connection()->hasXInput2())connection()->xi2SelectDeviceEvents(m_window);setWindowState(window()->windowStates());setWindowFlags(window()->flags());setWindowTitle(window()->title());// force sync to read outstanding requests - see QTBUG-29106connection()->sync();#if QT_CONFIG(draganddrop)connection()->drag()->dndEnable(this,true);#endifconst qreal opacity =qt_window_private(window())->opacity;if(!qFuzzyCompare(opacity,qreal(1.0)))setOpacity(opacity);setMask(QHighDpi::toNativeLocalRegion(window()->mask(),window()));if(window()->isTopLevel())setWindowIcon(window()->icon());if(window()->dynamicPropertyNames().contains(wm_window_role_property_id)) { QByteArray wmWindowRole =window()->property(wm_window_role_property_id).toByteArray();setWindowRole(QString::fromLatin1(wmWindowRole));}if(m_trayIconWindow) m_embedded =requestSystemTrayWindowDock();if(m_window != old_m_window) {if(!m_wmTransientForChildren.isEmpty()) { QList<QPointer<QXcbWindow>> transientChildren = m_wmTransientForChildren; m_wmTransientForChildren.clear();for(auto transientChild : transientChildren) {if(transientChild) transientChild->updateWmTransientFor();}}}}QXcbWindow::~QXcbWindow(){destroy();}QXcbForeignWindow::QXcbForeignWindow(QWindow *window, WId nativeHandle):QXcbWindow(window){ m_window = nativeHandle;// Reflect the foreign window's geometry as our ownif(auto geometry =Q_XCB_REPLY(xcb_get_geometry,xcb_connection(), m_window)) { QRect nativeGeometry(geometry->x, geometry->y, geometry->width, geometry->height);QPlatformWindow::setGeometry(nativeGeometry);}// And reparent, if we have a parent alreadyif(QPlatformWindow::parent())setParent(QPlatformWindow::parent());}QXcbForeignWindow::~QXcbForeignWindow(){if(QPlatformWindow::parent())setParent(nullptr);// Clear window so that destroy() does not affect it m_window =0;if(connection()->mouseGrabber() ==this)connection()->setMouseGrabber(nullptr);if(connection()->mousePressWindow() ==this)connection()->setMousePressWindow(nullptr);}voidQXcbWindow::destroy(){if(connection()->focusWindow() ==this)doFocusOut();if(connection()->mouseGrabber() ==this)connection()->setMouseGrabber(nullptr);if(connection()->mousePressWindow() ==this)connection()->setMousePressWindow(nullptr);if(m_syncCounter &&connection()->hasXSync())xcb_sync_destroy_counter(xcb_connection(), m_syncCounter);if(m_window) {if(m_netWmUserTimeWindow) {xcb_delete_property(xcb_connection(), m_window,atom(QXcbAtom::Atom_NET_WM_USER_TIME_WINDOW));// Some window managers, like metacity, do XSelectInput on the _NET_WM_USER_TIME_WINDOW window,// without trapping BadWindow (which crashes when the user time window is destroyed).connection()->sync();xcb_destroy_window(xcb_connection(), m_netWmUserTimeWindow); m_netWmUserTimeWindow = XCB_NONE;}connection()->removeWindowEventListener(m_window);xcb_destroy_window(xcb_connection(), m_window); m_window =0;} m_mapped =false; m_recreationReasons = RecreationNotNeeded;if(m_pendingSyncRequest) m_pendingSyncRequest->invalidate();}voidQXcbWindow::setGeometry(const QRect &rect){QPlatformWindow::setGeometry(rect);propagateSizeHints(); QXcbScreen *currentScreen =xcbScreen(); QXcbScreen *newScreen =QPlatformWindow::parent() ?parentScreen() :static_cast<QXcbScreen*>(screenForGeometry(rect));if(!newScreen) newScreen =xcbScreen();if(newScreen != currentScreen)QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->QPlatformScreen::screen());if(qt_window_private(window())->positionAutomatic) {const quint32 mask = XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;const qint32 values[] = { qBound<qint32>(1, rect.width(), XCOORD_MAX), qBound<qint32>(1, rect.height(), XCOORD_MAX),};xcb_configure_window(xcb_connection(), m_window, mask,reinterpret_cast<const quint32*>(values));}else{const quint32 mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;const qint32 values[] = { qBound<qint32>(-XCOORD_MAX, rect.x(), XCOORD_MAX), qBound<qint32>(-XCOORD_MAX, rect.y(), XCOORD_MAX), qBound<qint32>(1, rect.width(), XCOORD_MAX), qBound<qint32>(1, rect.height(), XCOORD_MAX),};xcb_configure_window(xcb_connection(), m_window, mask,reinterpret_cast<const quint32*>(values));if(window()->parent() && !window()->transientParent()) {// Wait for server reply for parented windows to ensure that a few window// moves will come as a one event. This is important when native widget is// moved a few times in X and Y directions causing native scroll. Widget// must get single event to not trigger unwanted widget position changes// and then expose events causing backingstore flushes with incorrect// offset causing image crruption.connection()->sync();}}xcb_flush(xcb_connection());} QMargins QXcbWindow::frameMargins()const{if(m_dirtyFrameMargins) {if(connection()->wmSupport()->isSupportedByWM(atom(QXcbAtom::Atom_NET_FRAME_EXTENTS))) {auto reply =Q_XCB_REPLY(xcb_get_property,xcb_connection(),false, m_window,atom(QXcbAtom::Atom_NET_FRAME_EXTENTS), XCB_ATOM_CARDINAL,0,4);if(reply && reply->type == XCB_ATOM_CARDINAL && reply->format ==32&& reply->value_len ==4) { quint32 *data = (quint32 *)xcb_get_property_value(reply.get());// _NET_FRAME_EXTENTS format is left, right, top, bottom m_frameMargins =QMargins(data[0], data[2], data[1], data[3]); m_dirtyFrameMargins =false;return m_frameMargins;}}// _NET_FRAME_EXTENTS property is not available, so// walk up the window tree to get the frame parent xcb_window_t window = m_window; xcb_window_t parent = m_window;bool foundRoot =false;const QList<xcb_window_t> &virtualRoots =connection()->wmSupport()->virtualRoots();while(!foundRoot) {auto reply =Q_XCB_REPLY_UNCHECKED(xcb_query_tree,xcb_connection(), parent);if(reply) {if(reply->root == reply->parent || virtualRoots.indexOf(reply->parent) != -1|| reply->parent == XCB_WINDOW_NONE) { foundRoot =true;}else{ window = parent; parent = reply->parent;}}else{ m_dirtyFrameMargins =false; m_frameMargins =QMargins();return m_frameMargins;}} QPoint offset;auto reply =Q_XCB_REPLY(xcb_translate_coordinates,xcb_connection(), window, parent,0,0);if(reply) { offset =QPoint(reply->dst_x, reply->dst_y);}auto geom =Q_XCB_REPLY(xcb_get_geometry,xcb_connection(), parent);if(geom) {// --// add the border_width for the window managers frame... some window managers// do not use a border_width of zero for their frames, and if we the left and// top strut, we ensure that pos() is absolutely correct. frameGeometry()// will still be incorrect though... perhaps i should have foffset as well, to// indicate the frame offset (equal to the border_width on X).// - Brad// -- copied from qwidget_x11.cppint left = offset.x() + geom->border_width;int top = offset.y() + geom->border_width;int right = geom->width + geom->border_width -geometry().width() - offset.x();int bottom = geom->height + geom->border_width -geometry().height() - offset.y(); m_frameMargins =QMargins(left, top, right, bottom);} m_dirtyFrameMargins =false;}return m_frameMargins;}voidQXcbWindow::setVisible(bool visible){if(visible)show();elsehide();}voidQXcbWindow::updateWmTransientFor(){ xcb_window_t transientXcbParent = XCB_NONE;if(isTransient(window())) { QWindow *tp =window()->transientParent();if(tp && tp->handle()) { QXcbWindow *handle =static_cast<QXcbWindow *>(tp->handle()); transientXcbParent = tp->handle()->winId();if(transientXcbParent) { handle->registerWmTransientForChild(this);qCDebug(lcQpaXcbWindow) << Q_FUNC_INFO <<static_cast<QPlatformWindow *>(handle)<<" registerWmTransientForChild "<<static_cast<QPlatformWindow *>(this);}}// Default to client leader if there is no transient parent, else modal dialogs can// be hidden by their parents.if(!transientXcbParent) transientXcbParent =connection()->clientLeader();if(transientXcbParent) {// ICCCM 4.1.2.6xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window, XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW,32,1, &transientXcbParent);qCDebug(lcQpaXcbWindow,"0x%x added XCB_ATOM_WM_TRANSIENT_FOR 0x%x", m_window, transientXcbParent);}}if(!transientXcbParent)xcb_delete_property(xcb_connection(), m_window, XCB_ATOM_WM_TRANSIENT_FOR);}voidQXcbWindow::registerWmTransientForChild(QXcbWindow *child){if(!child)return;if(!m_wmTransientForChildren.contains(child)) m_wmTransientForChildren.append(child);}voidQXcbWindow::show(){if(window()->isTopLevel()) {if(m_recreationReasons != RecreationNotNeeded) {qCDebug(lcQpaWindow) <<"QXcbWindow: need to recreate window"<<window() << m_recreationReasons;create(); m_recreationReasons = RecreationNotNeeded;}// update WM_NORMAL_HINTSpropagateSizeHints();// update WM_TRANSIENT_FORupdateWmTransientFor();// update _NET_WM_STATEsetNetWmStateOnUnmappedWindow();}// QWidget-attribute Qt::WA_ShowWithoutActivating.constauto showWithoutActivating =window()->property("_q_showWithoutActivating");if(showWithoutActivating.isValid() && showWithoutActivating.toBool())updateNetWmUserTime(0);else if(connection()->time() != XCB_TIME_CURRENT_TIME)updateNetWmUserTime(connection()->time());if(m_trayIconWindow)return;// defer showing until XEMBED_EMBEDDED_NOTIFYxcb_map_window(xcb_connection(), m_window);if(QGuiApplication::modalWindow() ==window())requestActivateWindow();xcbScreen()->windowShown(this);connection()->sync();}voidQXcbWindow::hide(){xcb_unmap_window(xcb_connection(), m_window);// send synthetic UnmapNotify event according to icccm 4.1.4 q_padded_xcb_event<xcb_unmap_notify_event_t> event = {}; event.response_type = XCB_UNMAP_NOTIFY; event.event =xcbScreen()->root(); event.window = m_window; event.from_configure =false;xcb_send_event(xcb_connection(),false,xcbScreen()->root(), XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char*)&event);xcb_flush(xcb_connection());if(connection()->mouseGrabber() ==this)connection()->setMouseGrabber(nullptr);if(QPlatformWindow *w =connection()->mousePressWindow()) {// Unset mousePressWindow when it (or one of its parents) is unmappedwhile(w) {if(w ==this) {connection()->setMousePressWindow(nullptr);break;} w = w->parent();}} m_mapped =false;// Hiding a modal window doesn't send an enter event to its transient parent when the// mouse is already over the parent window, so the enter event must be emulated.if(window()->isModal()) {// Get the cursor position at modal window screenconst QPoint nativePos =xcbScreen()->cursor()->pos();const QPoint cursorPos =QHighDpi::fromNativePixels(nativePos,xcbScreen()->screenForPosition(nativePos)->screen());// Find the top level window at cursor position.// Don't use QGuiApplication::topLevelAt(): search only the virtual siblings of this window's screen QWindow *enterWindow =nullptr;constauto screens =xcbScreen()->virtualSiblings();for(QPlatformScreen *screen : screens) {if(screen->geometry().contains(cursorPos)) {const QPoint devicePosition =QHighDpi::toNativePixels(cursorPos, screen->screen()); enterWindow = screen->topLevelAt(devicePosition);break;}}if(enterWindow && enterWindow !=window()) {// Find the child window at cursor position, otherwise use the top level windowif(QWindow *childWindow =childWindowAt(enterWindow, cursorPos)) enterWindow = childWindow;const QPoint localPos = enterWindow->mapFromGlobal(cursorPos);QWindowSystemInterface::handleEnterEvent(enterWindow, localPos *QHighDpiScaling::factor(enterWindow), nativePos);}}}boolQXcbWindow::relayFocusToModalWindow()const{ QWindow *w =static_cast<QWindowPrivate *>(QObjectPrivate::get(window()))->eventReceiver();// get top-level windowwhile(w && w->parent()) w = w->parent(); QWindow *modalWindow =nullptr;const bool blocked =QGuiApplicationPrivate::instance()->isWindowBlocked(w, &modalWindow);if(blocked && modalWindow != w) { modalWindow->requestActivate();connection()->flush();return true;}return false;}voidQXcbWindow::doFocusIn(){if(relayFocusToModalWindow())return; QWindow *w =static_cast<QWindowPrivate *>(QObjectPrivate::get(window()))->eventReceiver();connection()->setFocusWindow(w);QWindowSystemInterface::handleFocusWindowChanged(w,Qt::ActiveWindowFocusReason);}voidQXcbWindow::doFocusOut(){connection()->setFocusWindow(nullptr);relayFocusToModalWindow();// Do not set the active window to nullptr if there is a FocusIn coming.connection()->focusInTimer().start();}struct QtMotifWmHints { quint32 flags, functions, decorations; qint32 input_mode;// unused quint32 status;// unused};enum{ MWM_HINTS_FUNCTIONS = (1L<<0), MWM_FUNC_ALL = (1L<<0), MWM_FUNC_RESIZE = (1L<<1), MWM_FUNC_MOVE = (1L<<2), MWM_FUNC_MINIMIZE = (1L<<3), MWM_FUNC_MAXIMIZE = (1L<<4), MWM_FUNC_CLOSE = (1L<<5), MWM_HINTS_DECORATIONS = (1L<<1), MWM_DECOR_ALL = (1L<<0), MWM_DECOR_BORDER = (1L<<1), MWM_DECOR_RESIZEH = (1L<<2), MWM_DECOR_TITLE = (1L<<3), MWM_DECOR_MENU = (1L<<4), MWM_DECOR_MINIMIZE = (1L<<5), MWM_DECOR_MAXIMIZE = (1L<<6),};QXcbWindow::NetWmStates QXcbWindow::netWmStates(){ NetWmStates result;auto reply =Q_XCB_REPLY_UNCHECKED(xcb_get_property,xcb_connection(),0, m_window,atom(QXcbAtom::Atom_NET_WM_STATE), XCB_ATOM_ATOM,0,1024);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.get()));const xcb_atom_t *statesEnd = states + reply->length;if(statesEnd !=std::find(states, statesEnd,atom(QXcbAtom::Atom_NET_WM_STATE_ABOVE))) result |= NetWmStateAbove;if(statesEnd !=std::find(states, statesEnd,atom(QXcbAtom::Atom_NET_WM_STATE_BELOW))) result |= NetWmStateBelow;if(statesEnd !=std::find(states, statesEnd,atom(QXcbAtom::Atom_NET_WM_STATE_FULLSCREEN))) result |= NetWmStateFullScreen;if(statesEnd !=std::find(states, statesEnd,atom(QXcbAtom::Atom_NET_WM_STATE_MAXIMIZED_HORZ))) result |= NetWmStateMaximizedHorz;if(statesEnd !=std::find(states, statesEnd,atom(QXcbAtom::Atom_NET_WM_STATE_MAXIMIZED_VERT))) result |= NetWmStateMaximizedVert;if(statesEnd !=std::find(states, statesEnd,atom(QXcbAtom::Atom_NET_WM_STATE_MODAL))) result |= NetWmStateModal;if(statesEnd !=std::find(states, statesEnd,atom(QXcbAtom::Atom_NET_WM_STATE_STAYS_ON_TOP))) result |= NetWmStateStaysOnTop;if(statesEnd !=std::find(states, statesEnd,atom(QXcbAtom::Atom_NET_WM_STATE_DEMANDS_ATTENTION))) result |= NetWmStateDemandsAttention;if(statesEnd !=std::find(states, statesEnd,atom(QXcbAtom::Atom_NET_WM_STATE_HIDDEN))) result |= NetWmStateHidden;}else{qCDebug(lcQpaXcb,"getting net wm state (%x), empty\n", m_window);}return result;}voidQXcbWindow::setWindowFlags(Qt::WindowFlags flags){Qt::WindowType type =static_cast<Qt::WindowType>(int(flags &Qt::WindowType_Mask));if(type ==Qt::ToolTip) flags |=Qt::WindowStaysOnTopHint |Qt::FramelessWindowHint |Qt::X11BypassWindowManagerHint;if(type ==Qt::Popup) flags |=Qt::X11BypassWindowManagerHint;Qt::WindowFlags oldflags =window()->flags();if((oldflags &Qt::WindowStaysOnTopHint) != (flags &Qt::WindowStaysOnTopHint)) m_recreationReasons |= WindowStaysOnTopHintChanged;if((oldflags &Qt::WindowStaysOnBottomHint) != (flags &Qt::WindowStaysOnBottomHint)) m_recreationReasons |= WindowStaysOnBottomHintChanged;const quint32 mask = XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK;const quint32 values[] = {// XCB_CW_OVERRIDE_REDIRECT(flags &Qt::BypassWindowManagerHint) ?1u:0,// XCB_CW_EVENT_MASK(flags &Qt::WindowTransparentForInput) ? transparentForInputEventMask : defaultEventMask };xcb_change_window_attributes(xcb_connection(),xcb_window(), mask, values); WindowTypes wmWindowTypes;if(window()->dynamicPropertyNames().contains(wm_window_type_property_id)) { wmWindowTypes =static_cast<WindowTypes>( qvariant_cast<int>(window()->property(wm_window_type_property_id)));}setWmWindowType(wmWindowTypes, flags);setNetWmState(flags);setMotifWmHints(flags);setTransparentForMouseEvents(flags &Qt::WindowTransparentForInput);updateDoesNotAcceptFocus(flags &Qt::WindowDoesNotAcceptFocus);}voidQXcbWindow::setMotifWmHints(Qt::WindowFlags flags){Qt::WindowType type =static_cast<Qt::WindowType>(int(flags &Qt::WindowType_Mask)); QtMotifWmHints mwmhints;memset(&mwmhints,0,sizeof(mwmhints));if(type !=Qt::SplashScreen) { mwmhints.flags |= MWM_HINTS_DECORATIONS;bool customize = flags &Qt::CustomizeWindowHint;if(type ==Qt::Window && !customize) {constQt::WindowFlags defaultFlags =Qt::WindowSystemMenuHint |Qt::WindowMinMaxButtonsHint |Qt::WindowCloseButtonHint;if(!(flags & defaultFlags)) flags |= defaultFlags;}if(!(flags &Qt::FramelessWindowHint) && !(customize && !(flags &Qt::WindowTitleHint))) { mwmhints.decorations |= MWM_DECOR_BORDER; mwmhints.decorations |= MWM_DECOR_RESIZEH; mwmhints.decorations |= MWM_DECOR_TITLE;if(flags &Qt::WindowSystemMenuHint) mwmhints.decorations |= MWM_DECOR_MENU;if(flags &Qt::WindowMinimizeButtonHint) { mwmhints.decorations |= MWM_DECOR_MINIMIZE; mwmhints.functions |= MWM_FUNC_MINIMIZE;}if(flags &Qt::WindowMaximizeButtonHint) { mwmhints.decorations |= MWM_DECOR_MAXIMIZE; mwmhints.functions |= MWM_FUNC_MAXIMIZE;}if(flags &Qt::WindowCloseButtonHint) mwmhints.functions |= MWM_FUNC_CLOSE;}}else{// if type == Qt::SplashScreen mwmhints.decorations = MWM_DECOR_ALL;}if(mwmhints.functions !=0) { mwmhints.flags |= MWM_HINTS_FUNCTIONS; mwmhints.functions |= MWM_FUNC_MOVE | MWM_FUNC_RESIZE;}else{ mwmhints.functions = MWM_FUNC_ALL;}if(!(flags &Qt::FramelessWindowHint)&& flags &Qt::CustomizeWindowHint && flags &Qt::WindowTitleHint && !(flags &(Qt::WindowMinimizeButtonHint |Qt::WindowMaximizeButtonHint |Qt::WindowCloseButtonHint))){// a special case - only the titlebar without any button mwmhints.flags = MWM_HINTS_FUNCTIONS; mwmhints.functions = MWM_FUNC_MOVE | MWM_FUNC_RESIZE; mwmhints.decorations =0;}if(mwmhints.flags) {xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,atom(QXcbAtom::Atom_MOTIF_WM_HINTS),atom(QXcbAtom::Atom_MOTIF_WM_HINTS),32,5,&mwmhints);}else{xcb_delete_property(xcb_connection(), m_window,atom(QXcbAtom::Atom_MOTIF_WM_HINTS));}}voidQXcbWindow::setNetWmState(bool set, xcb_atom_t one, xcb_atom_t two){ xcb_client_message_event_t event; event.response_type = XCB_CLIENT_MESSAGE; event.format =32; event.sequence =0; event.window = m_window; event.type =atom(QXcbAtom::Atom_NET_WM_STATE); event.data.data32[0] = set ?1:0; event.data.data32[1] = one; event.data.data32[2] = two; event.data.data32[3] =0; event.data.data32[4] =0;xcb_send_event(xcb_connection(),0,xcbScreen()->root(), XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,(const char*)&event);}voidQXcbWindow::setNetWmState(Qt::WindowStates state){if((m_windowState ^ state) &Qt::WindowMaximized) {setNetWmState(state &Qt::WindowMaximized,atom(QXcbAtom::Atom_NET_WM_STATE_MAXIMIZED_HORZ),atom(QXcbAtom::Atom_NET_WM_STATE_MAXIMIZED_VERT));}if((m_windowState ^ state) &Qt::WindowFullScreen)setNetWmState(state &Qt::WindowFullScreen,atom(QXcbAtom::Atom_NET_WM_STATE_FULLSCREEN));}voidQXcbWindow::setNetWmState(Qt::WindowFlags flags){setNetWmState(flags &Qt::WindowStaysOnTopHint,atom(QXcbAtom::Atom_NET_WM_STATE_ABOVE),atom(QXcbAtom::Atom_NET_WM_STATE_STAYS_ON_TOP));setNetWmState(flags &Qt::WindowStaysOnBottomHint,atom(QXcbAtom::Atom_NET_WM_STATE_BELOW));}voidQXcbWindow::setNetWmStateOnUnmappedWindow(){if(Q_UNLIKELY(m_mapped))qCDebug(lcQpaXcb()) <<"internal info: "<< Q_FUNC_INFO <<"called on mapped window"; NetWmStates states;constQt::WindowFlags flags =window()->flags();if(flags &Qt::WindowStaysOnTopHint) { states |= NetWmStateAbove; states |= NetWmStateStaysOnTop;}else if(flags &Qt::WindowStaysOnBottomHint) { states |= NetWmStateBelow;}if(window()->windowStates() &Qt::WindowMinimized) states |= NetWmStateHidden;if(window()->windowStates() &Qt::WindowFullScreen) states |= NetWmStateFullScreen;if(window()->windowStates() &Qt::WindowMaximized) { states |= NetWmStateMaximizedHorz; states |= NetWmStateMaximizedVert;}if(window()->modality() !=Qt::NonModal) states |= NetWmStateModal;// According to EWMH:// "The Window Manager should remove _NET_WM_STATE whenever a window is withdrawn".// Which means that we don't have to read this property before changing it on a withdrawn// window. But there are situations where users want to adjust this property as well// (e4cea305ed2ba3c9f580bf9d16c59a1048af0e8a), so instead of overwriting the property// we first read it and then merge our hints with the existing values, allowing a user// to set custom hints. QList<xcb_atom_t> atoms;auto reply =Q_XCB_REPLY_UNCHECKED(xcb_get_property,xcb_connection(),0, m_window,atom(QXcbAtom::Atom_NET_WM_STATE), XCB_ATOM_ATOM,0,1024);if(reply && reply->format ==32&& reply->type == XCB_ATOM_ATOM && reply->value_len >0) {const xcb_atom_t *data =static_cast<const xcb_atom_t *>(xcb_get_property_value(reply.get())); atoms.resize(reply->value_len);memcpy((void*)&atoms.first(), (void*)data, reply->value_len *sizeof(xcb_atom_t));}if(states & NetWmStateAbove && !atoms.contains(atom(QXcbAtom::Atom_NET_WM_STATE_ABOVE))) atoms.push_back(atom(QXcbAtom::Atom_NET_WM_STATE_ABOVE));if(states & NetWmStateBelow && !atoms.contains(atom(QXcbAtom::Atom_NET_WM_STATE_BELOW))) atoms.push_back(atom(QXcbAtom::Atom_NET_WM_STATE_BELOW));if(states & NetWmStateHidden && !atoms.contains(atom(QXcbAtom::Atom_NET_WM_STATE_HIDDEN))) atoms.push_back(atom(QXcbAtom::Atom_NET_WM_STATE_HIDDEN));if(states & NetWmStateFullScreen && !atoms.contains(atom(QXcbAtom::Atom_NET_WM_STATE_FULLSCREEN))) atoms.push_back(atom(QXcbAtom::Atom_NET_WM_STATE_FULLSCREEN));if(states & NetWmStateMaximizedHorz && !atoms.contains(atom(QXcbAtom::Atom_NET_WM_STATE_MAXIMIZED_HORZ))) atoms.push_back(atom(QXcbAtom::Atom_NET_WM_STATE_MAXIMIZED_HORZ));if(states & NetWmStateMaximizedVert && !atoms.contains(atom(QXcbAtom::Atom_NET_WM_STATE_MAXIMIZED_VERT))) atoms.push_back(atom(QXcbAtom::Atom_NET_WM_STATE_MAXIMIZED_VERT));if(states & NetWmStateModal && !atoms.contains(atom(QXcbAtom::Atom_NET_WM_STATE_MODAL))) atoms.push_back(atom(QXcbAtom::Atom_NET_WM_STATE_MODAL));if(states & NetWmStateStaysOnTop && !atoms.contains(atom(QXcbAtom::Atom_NET_WM_STATE_STAYS_ON_TOP))) atoms.push_back(atom(QXcbAtom::Atom_NET_WM_STATE_STAYS_ON_TOP));if(states & NetWmStateDemandsAttention && !atoms.contains(atom(QXcbAtom::Atom_NET_WM_STATE_DEMANDS_ATTENTION))) atoms.push_back(atom(QXcbAtom::Atom_NET_WM_STATE_DEMANDS_ATTENTION));if(atoms.isEmpty()) {xcb_delete_property(xcb_connection(), m_window,atom(QXcbAtom::Atom_NET_WM_STATE));}else{xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,atom(QXcbAtom::Atom_NET_WM_STATE), XCB_ATOM_ATOM,32, atoms.size(), atoms.constData());}xcb_flush(xcb_connection());}voidQXcbWindow::setWindowState(Qt::WindowStates state){if(state == m_windowState)return;Qt::WindowStates unsetState = m_windowState & ~state;Qt::WindowStates newState = state & ~m_windowState;// unset old stateif(unsetState &Qt::WindowMinimized)xcb_map_window(xcb_connection(), m_window);if(unsetState &Qt::WindowMaximized)setNetWmState(false,atom(QXcbAtom::Atom_NET_WM_STATE_MAXIMIZED_HORZ),atom(QXcbAtom::Atom_NET_WM_STATE_MAXIMIZED_VERT));if(unsetState &Qt::WindowFullScreen)setNetWmState(false,atom(QXcbAtom::Atom_NET_WM_STATE_FULLSCREEN));// set new stateif(newState &Qt::WindowMinimized) {{ xcb_client_message_event_t event; event.response_type = XCB_CLIENT_MESSAGE; event.format =32; event.sequence =0; event.window = m_window; event.type =atom(QXcbAtom::AtomWM_CHANGE_STATE); event.data.data32[0] = XCB_ICCCM_WM_STATE_ICONIC; event.data.data32[1] =0; event.data.data32[2] =0; event.data.data32[3] =0; event.data.data32[4] =0;xcb_send_event(xcb_connection(),0,xcbScreen()->root(), XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,(const char*)&event);} m_minimized =true;}// set Maximized && FullScreen state if needsetNetWmState(state); xcb_get_property_cookie_t cookie =xcb_icccm_get_wm_hints_unchecked(xcb_connection(), m_window); xcb_icccm_wm_hints_t hints;if(xcb_icccm_get_wm_hints_reply(xcb_connection(), cookie, &hints,nullptr)) {if(state &Qt::WindowMinimized)xcb_icccm_wm_hints_set_iconic(&hints);elsexcb_icccm_wm_hints_set_normal(&hints);xcb_icccm_set_wm_hints(xcb_connection(), m_window, &hints);}connection()->sync(); m_windowState = state;}voidQXcbWindow::updateNetWmUserTime(xcb_timestamp_t timestamp){ xcb_window_t wid = m_window;// If timestamp == 0, then it means that the window should not be// initially activated. Don't update global user time for this// special case.if(timestamp !=0)connection()->setNetWmUserTime(timestamp);const bool isSupportedByWM =connection()->wmSupport()->isSupportedByWM(atom(QXcbAtom::Atom_NET_WM_USER_TIME_WINDOW));if(m_netWmUserTimeWindow || isSupportedByWM) {if(!m_netWmUserTimeWindow) { m_netWmUserTimeWindow =xcb_generate_id(xcb_connection());xcb_create_window(xcb_connection(), XCB_COPY_FROM_PARENT,// depth -- same as root m_netWmUserTimeWindow,// window id m_window,// parent window id-1, -1,1,1,0,// border width XCB_WINDOW_CLASS_INPUT_OUTPUT,// window class m_visualId,// visual0,// value masknullptr);// value list wid = m_netWmUserTimeWindow;xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,atom(QXcbAtom::Atom_NET_WM_USER_TIME_WINDOW), XCB_ATOM_WINDOW,32,1, &m_netWmUserTimeWindow);xcb_delete_property(xcb_connection(), m_window,atom(QXcbAtom::Atom_NET_WM_USER_TIME));QXcbWindow::setWindowTitle(connection(), m_netWmUserTimeWindow,QStringLiteral("Qt NET_WM User Time Window"));}else if(!isSupportedByWM) {// WM no longer supports it, then we should remove the// _NET_WM_USER_TIME_WINDOW atom.xcb_delete_property(xcb_connection(), m_window,atom(QXcbAtom::Atom_NET_WM_USER_TIME_WINDOW));xcb_destroy_window(xcb_connection(), m_netWmUserTimeWindow); m_netWmUserTimeWindow = XCB_NONE;}else{ wid = m_netWmUserTimeWindow;}}xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, wid,atom(QXcbAtom::Atom_NET_WM_USER_TIME), XCB_ATOM_CARDINAL,32,1, &timestamp);}voidQXcbWindow::setTransparentForMouseEvents(bool transparent){if(!connection()->hasXFixes() || transparent == m_transparent)return; xcb_rectangle_t rectangle; xcb_rectangle_t *rect =nullptr;int nrect =0;if(!transparent) { rectangle.x =0; rectangle.y =0; rectangle.width =geometry().width(); rectangle.height =geometry().height(); rect = &rectangle; nrect =1;} xcb_xfixes_region_t region =xcb_generate_id(xcb_connection());xcb_xfixes_create_region(xcb_connection(), region, nrect, rect);xcb_xfixes_set_window_shape_region_checked(xcb_connection(), m_window, XCB_SHAPE_SK_INPUT,0,0, region);xcb_xfixes_destroy_region(xcb_connection(), region); m_transparent = transparent;}voidQXcbWindow::updateDoesNotAcceptFocus(bool doesNotAcceptFocus){ xcb_get_property_cookie_t cookie =xcb_icccm_get_wm_hints_unchecked(xcb_connection(), m_window); xcb_icccm_wm_hints_t hints;if(!xcb_icccm_get_wm_hints_reply(xcb_connection(), cookie, &hints,nullptr))return;xcb_icccm_wm_hints_set_input(&hints, !doesNotAcceptFocus);xcb_icccm_set_wm_hints(xcb_connection(), m_window, &hints);} WId QXcbWindow::winId()const{return m_window;}voidQXcbWindow::setParent(const QPlatformWindow *parent){ QPoint topLeft =geometry().topLeft(); xcb_window_t xcb_parent_id;if(parent) {const QXcbWindow *qXcbParent =static_cast<const QXcbWindow *>(parent); xcb_parent_id = qXcbParent->xcb_window(); m_embedded = qXcbParent->isForeignWindow();}else{ xcb_parent_id =xcbScreen()->root(); m_embedded =false;}xcb_reparent_window(xcb_connection(),xcb_window(), xcb_parent_id, topLeft.x(), topLeft.y());connection()->sync();}voidQXcbWindow::setWindowTitle(const QString &title){setWindowTitle(connection(), m_window, title);} QString QXcbWindow::windowTitle()const{returnwindowTitle(connection(), m_window);}voidQXcbWindow::setWindowIconText(const QString &title){const QByteArray ba = title.toUtf8();xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,atom(QXcbAtom::Atom_NET_WM_ICON_NAME),atom(QXcbAtom::AtomUTF8_STRING),8, ba.size(), ba.constData());}voidQXcbWindow::setWindowIcon(const QIcon &icon){ QList<quint32> icon_data;const uint32_t sizeLimit =xcb_get_maximum_request_length(xcb_connection());if(!icon.isNull()) { QList<QSize> availableSizes = icon.availableSizes();if(availableSizes.isEmpty()) {// try to use default sizes since the icon can be a scalable image like svg. availableSizes.push_back(QSize(16,16)); availableSizes.push_back(QSize(32,32)); availableSizes.push_back(QSize(64,64)); availableSizes.push_back(QSize(128,128));}for(int i =0; i < availableSizes.size(); ++i) { QSize size = availableSizes.at(i); QPixmap pixmap = icon.pixmap(size);if(!pixmap.isNull()) { QImage image = pixmap.toImage().convertToFormat(QImage::Format_ARGB32);int pos = icon_data.size();int newSize = pos +2+ image.width()*image.height();// In the absence of the BIG-REQUESTS extension, or with too big DPR,// the size of icon data is too big for the xcb request very easily.if(quint64(newSize) >quint64(sizeLimit))break; icon_data.resize(newSize); icon_data[pos++] = image.width(); icon_data[pos++] = image.height();memcpy(icon_data.data() + pos, image.bits(), image.width()*image.height()*4);}}}if(!icon_data.isEmpty()) {// Ignore icon exceeding maximum xcb request lengthif(quint64(icon_data.size()) >quint64(sizeLimit)) {qWarning() <<"Ignoring window icon"<< icon_data.size()<<"exceeds maximum xcb request length"<< sizeLimit;return;}xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,atom(QXcbAtom::Atom_NET_WM_ICON),atom(QXcbAtom::AtomCARDINAL),32, icon_data.size(),(unsigned char*) icon_data.data());}else{xcb_delete_property(xcb_connection(), m_window,atom(QXcbAtom::Atom_NET_WM_ICON));}}voidQXcbWindow::raise(){const quint32 mask = XCB_CONFIG_WINDOW_STACK_MODE;const quint32 values[] = { XCB_STACK_MODE_ABOVE };xcb_configure_window(xcb_connection(), m_window, mask, values);}voidQXcbWindow::lower(){const quint32 mask = XCB_CONFIG_WINDOW_STACK_MODE;const quint32 values[] = { XCB_STACK_MODE_BELOW };xcb_configure_window(xcb_connection(), m_window, mask, values);}voidQXcbWindow::propagateSizeHints(){// update WM_NORMAL_HINTS xcb_size_hints_t hints;memset(&hints,0,sizeof(hints));const QRect rect =geometry(); QWindowPrivate *win =qt_window_private(window());if(!win->positionAutomatic)xcb_icccm_size_hints_set_position(&hints,true, rect.x(), rect.y());if(rect.width() < QWINDOWSIZE_MAX || rect.height() < QWINDOWSIZE_MAX)xcb_icccm_size_hints_set_size(&hints,true, rect.width(), rect.height());/* Gravity describes how to interpret x and y values the next time window needs to be positioned on a screen. XCB_GRAVITY_STATIC : the left top corner of the client window XCB_GRAVITY_NORTH_WEST : the left top corner of the frame window */auto gravity = win->positionPolicy ==QWindowPrivate::WindowFrameInclusive ? XCB_GRAVITY_NORTH_WEST : XCB_GRAVITY_STATIC;xcb_icccm_size_hints_set_win_gravity(&hints, gravity); QSize minimumSize =windowMinimumSize(); QSize maximumSize =windowMaximumSize(); QSize baseSize =windowBaseSize(); QSize sizeIncrement =windowSizeIncrement();if(minimumSize.width() >0|| minimumSize.height() >0)xcb_icccm_size_hints_set_min_size(&hints,qMin(XCOORD_MAX,minimumSize.width()),qMin(XCOORD_MAX,minimumSize.height()));if(maximumSize.width() < QWINDOWSIZE_MAX || maximumSize.height() < QWINDOWSIZE_MAX)xcb_icccm_size_hints_set_max_size(&hints,qMin(XCOORD_MAX, maximumSize.width()),qMin(XCOORD_MAX, maximumSize.height()));if(sizeIncrement.width() >0|| sizeIncrement.height() >0) {if(!baseSize.isNull() && baseSize.isValid())xcb_icccm_size_hints_set_base_size(&hints, baseSize.width(), baseSize.height());xcb_icccm_size_hints_set_resize_inc(&hints, sizeIncrement.width(), sizeIncrement.height());}xcb_icccm_set_wm_normal_hints(xcb_connection(), m_window, &hints); m_sizeHintsScaleFactor =QHighDpiScaling::factor(screen());}voidQXcbWindow::requestActivateWindow(){/* Never activate embedded windows; doing that would prevent the container * to re-gain the keyboard focus later. */if(m_embedded) {QPlatformWindow::requestActivateWindow();return;}{ QMutexLocker locker(&m_mappedMutex);if(!m_mapped) { m_deferredActivation =true;return;} m_deferredActivation =false;}updateNetWmUserTime(connection()->time()); QWindow *focusWindow =QGuiApplication::focusWindow(); xcb_window_t current = XCB_NONE;if(focusWindow) {if(QPlatformWindow *pw = focusWindow->handle()) current = pw->winId();}if(window()->isTopLevel()&& !(window()->flags() &Qt::X11BypassWindowManagerHint)&& (!focusWindow || !window()->isAncestorOf(focusWindow))&&connection()->wmSupport()->isSupportedByWM(atom(QXcbAtom::Atom_NET_ACTIVE_WINDOW))) { xcb_client_message_event_t event; event.response_type = XCB_CLIENT_MESSAGE; event.format =32; event.sequence =0; event.window = m_window; event.type =atom(QXcbAtom::Atom_NET_ACTIVE_WINDOW); event.data.data32[0] =1; event.data.data32[1] =connection()->time(); event.data.data32[2] = current; event.data.data32[3] =0; event.data.data32[4] =0;xcb_send_event(xcb_connection(),0,xcbScreen()->root(), XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,(const char*)&event);}else{xcb_set_input_focus(xcb_connection(), XCB_INPUT_FOCUS_PARENT, m_window,connection()->time());}connection()->sync();} QSurfaceFormat QXcbWindow::format()const{return m_format;}QXcbWindow::WindowTypes QXcbWindow::wmWindowTypes()const{ WindowTypes result;auto reply =Q_XCB_REPLY_UNCHECKED(xcb_get_property,xcb_connection(),0, m_window,atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE), XCB_ATOM_ATOM,0,1024);if(reply && reply->format ==32&& reply->type == XCB_ATOM_ATOM) {const xcb_atom_t *types =static_cast<const xcb_atom_t *>(xcb_get_property_value(reply.get()));const xcb_atom_t *types_end = types + reply->length;for(; types != types_end; types++) {QXcbAtom::Atom type =connection()->qatom(*types);switch(type) {caseQXcbAtom::Atom_NET_WM_WINDOW_TYPE_NORMAL: result |=WindowType::Normal;break;caseQXcbAtom::Atom_NET_WM_WINDOW_TYPE_DESKTOP: result |=WindowType::Desktop;break;caseQXcbAtom::Atom_NET_WM_WINDOW_TYPE_DOCK: result |=WindowType::Dock;break;caseQXcbAtom::Atom_NET_WM_WINDOW_TYPE_TOOLBAR: result |=WindowType::Toolbar;break;caseQXcbAtom::Atom_NET_WM_WINDOW_TYPE_MENU: result |=WindowType::Menu;break;caseQXcbAtom::Atom_NET_WM_WINDOW_TYPE_UTILITY: result |=WindowType::Utility;break;caseQXcbAtom::Atom_NET_WM_WINDOW_TYPE_SPLASH: result |=WindowType::Splash;break;caseQXcbAtom::Atom_NET_WM_WINDOW_TYPE_DIALOG: result |=WindowType::Dialog;break;caseQXcbAtom::Atom_NET_WM_WINDOW_TYPE_DROPDOWN_MENU: result |=WindowType::DropDownMenu;break;caseQXcbAtom::Atom_NET_WM_WINDOW_TYPE_POPUP_MENU: result |=WindowType::PopupMenu;break;caseQXcbAtom::Atom_NET_WM_WINDOW_TYPE_TOOLTIP: result |=WindowType::Tooltip;break;caseQXcbAtom::Atom_NET_WM_WINDOW_TYPE_NOTIFICATION: result |=WindowType::Notification;break;caseQXcbAtom::Atom_NET_WM_WINDOW_TYPE_COMBO: result |=WindowType::Combo;break;caseQXcbAtom::Atom_NET_WM_WINDOW_TYPE_DND: result |=WindowType::Dnd;break;caseQXcbAtom::Atom_KDE_NET_WM_WINDOW_TYPE_OVERRIDE: result |=WindowType::KdeOverride;break;default:break;}}}return result;}voidQXcbWindow::setWmWindowType(WindowTypes types,Qt::WindowFlags flags){ QList<xcb_atom_t> atoms;// manual selection 1 (these are never set by Qt and take precedence)if(types &WindowType::Normal) atoms.append(atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE_NORMAL));if(types &WindowType::Desktop) atoms.append(atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE_DESKTOP));if(types &WindowType::Dock) atoms.append(atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE_DOCK));if(types &WindowType::Notification) atoms.append(atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE_NOTIFICATION));// manual selection 2 (Qt uses these during auto selection);if(types &WindowType::Utility) atoms.append(atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE_UTILITY));if(types &WindowType::Splash) atoms.append(atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE_SPLASH));if(types &WindowType::Dialog) atoms.append(atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE_DIALOG));if(types &WindowType::Tooltip) atoms.append(atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE_TOOLTIP));if(types &WindowType::KdeOverride) atoms.append(atom(QXcbAtom::Atom_KDE_NET_WM_WINDOW_TYPE_OVERRIDE));// manual selection 3 (these can be set by Qt, but don't have a// corresponding Qt::WindowType). note that order of the *MENU// atoms is importantif(types &WindowType::Menu) atoms.append(atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE_MENU));if(types &WindowType::DropDownMenu) atoms.append(atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE_DROPDOWN_MENU));if(types &WindowType::PopupMenu) atoms.append(atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE_POPUP_MENU));if(types &WindowType::Toolbar) atoms.append(atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE_TOOLBAR));if(types &WindowType::Combo) atoms.append(atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE_COMBO));if(types &WindowType::Dnd) atoms.append(atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE_DND));// automatic selectionQt::WindowType type =static_cast<Qt::WindowType>(int(flags &Qt::WindowType_Mask));switch(type) {caseQt::Dialog:caseQt::Sheet:if(!(types &WindowType::Dialog)) atoms.append(atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE_DIALOG));break;caseQt::Tool:caseQt::Drawer:if(!(types &WindowType::Utility)) atoms.append(atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE_UTILITY));break;caseQt::ToolTip:if(!(types &WindowType::Tooltip)) atoms.append(atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE_TOOLTIP));break;caseQt::SplashScreen:if(!(types &WindowType::Splash)) atoms.append(atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE_SPLASH));break;default:break;}if((flags &Qt::FramelessWindowHint) && !(types &WindowType::KdeOverride)) {// override netwm type - quick and easy for KDE noborder atoms.append(atom(QXcbAtom::Atom_KDE_NET_WM_WINDOW_TYPE_OVERRIDE));}if(atoms.size() ==1&& atoms.first() ==atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE_NORMAL)) atoms.clear();else atoms.append(atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE_NORMAL));if(atoms.isEmpty()) {xcb_delete_property(xcb_connection(), m_window,atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE));}else{xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE), XCB_ATOM_ATOM,32, atoms.size(), atoms.constData());}xcb_flush(xcb_connection());}voidQXcbWindow::setWindowRole(const QString &role){ QByteArray roleData = role.toLatin1();xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,atom(QXcbAtom::AtomWM_WINDOW_ROLE), XCB_ATOM_STRING,8, roleData.size(), roleData.constData());}voidQXcbWindow::setParentRelativeBackPixmap(){const quint32 mask = XCB_CW_BACK_PIXMAP;const quint32 values[] = { XCB_BACK_PIXMAP_PARENT_RELATIVE };xcb_change_window_attributes(xcb_connection(), m_window, mask, values);}boolQXcbWindow::requestSystemTrayWindowDock(){if(!connection()->systemTrayTracker())return false;connection()->systemTrayTracker()->requestSystemTrayWindowDock(m_window);return true;}boolQXcbWindow::handleNativeEvent(xcb_generic_event_t *event){auto eventType =connection()->nativeInterface()->nativeEventType(); qintptr result =0;// Used only by MS WindowsreturnQWindowSystemInterface::handleNativeEvent(window(), eventType, event, &result);}voidQXcbWindow::handleExposeEvent(const xcb_expose_event_t *event){ QRect rect(event->x, event->y, event->width, event->height); m_exposeRegion |= rect;bool pending =true;connection()->eventQueue()->peek(QXcbEventQueue::PeekConsumeMatchAndContinue,[this, &pending](xcb_generic_event_t *event,int type) {if(type != XCB_EXPOSE)return false;auto expose =reinterpret_cast<xcb_expose_event_t *>(event);if(expose->window != m_window)return false;if(expose->count ==0) pending =false; m_exposeRegion |=QRect(expose->x, expose->y, expose->width, expose->height);free(expose);return true;});// if count is non-zero there are more expose events pendingif(event->count ==0|| !pending) {QWindowSystemInterface::handleExposeEvent(window(), m_exposeRegion); m_exposeRegion =QRegion();}}voidQXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *event){if(event->format !=32)return;if(event->type ==atom(QXcbAtom::AtomWM_PROTOCOLS)) { xcb_atom_t protocolAtom = event->data.data32[0];if(protocolAtom ==atom(QXcbAtom::AtomWM_DELETE_WINDOW)) {QWindowSystemInterface::handleCloseEvent(window());}else if(protocolAtom ==atom(QXcbAtom::AtomWM_TAKE_FOCUS)) {connection()->setTime(event->data.data32[1]);relayFocusToModalWindow();return;}else if(protocolAtom ==atom(QXcbAtom::Atom_NET_WM_PING)) {if(event->window ==xcbScreen()->root())return; xcb_client_message_event_t reply = *event; reply.response_type = XCB_CLIENT_MESSAGE; reply.window =xcbScreen()->root();xcb_send_event(xcb_connection(),0,xcbScreen()->root(), XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,(const char*)&reply);xcb_flush(xcb_connection());}else if(protocolAtom ==atom(QXcbAtom::Atom_NET_WM_SYNC_REQUEST)) {connection()->setTime(event->data.data32[1]); m_syncValue.lo = event->data.data32[2]; m_syncValue.hi = event->data.data32[3];if(connection()->hasXSync()) m_syncState = SyncReceived;#ifndef QT_NO_WHATSTHIS}else if(protocolAtom ==atom(QXcbAtom::Atom_NET_WM_CONTEXT_HELP)) {QWindowSystemInterface::handleEnterWhatsThisEvent();#endif}else{qCWarning(lcQpaXcb,"Unhandled WM_PROTOCOLS (%s)",connection()->atomName(protocolAtom).constData());}#if QT_CONFIG(draganddrop)}else if(event->type ==atom(QXcbAtom::AtomXdndEnter)) {connection()->drag()->handleEnter(this, event);}else if(event->type ==atom(QXcbAtom::AtomXdndPosition)) {connection()->drag()->handlePosition(this, event);}else if(event->type ==atom(QXcbAtom::AtomXdndLeave)) {connection()->drag()->handleLeave(this, event);}else if(event->type ==atom(QXcbAtom::AtomXdndDrop)) {connection()->drag()->handleDrop(this, event);#endif}else if(event->type ==atom(QXcbAtom::Atom_XEMBED)) {handleXEmbedMessage(event);}else if(event->type ==atom(QXcbAtom::Atom_NET_ACTIVE_WINDOW)) {doFocusIn();}else if(event->type ==atom(QXcbAtom::AtomMANAGER)|| event->type ==atom(QXcbAtom::Atom_NET_WM_STATE)|| event->type ==atom(QXcbAtom::AtomWM_CHANGE_STATE)) {// Ignore _NET_WM_STATE, MANAGER which are relate to tray icons// and other messages.}else if(event->type ==atom(QXcbAtom::Atom_COMPIZ_DECOR_PENDING)|| event->type ==atom(QXcbAtom::Atom_COMPIZ_DECOR_REQUEST)|| event->type ==atom(QXcbAtom::Atom_COMPIZ_DECOR_DELETE_PIXMAP)|| event->type ==atom(QXcbAtom::Atom_COMPIZ_TOOLKIT_ACTION)|| event->type ==atom(QXcbAtom::Atom_GTK_LOAD_ICONTHEMES)) {//silence the _COMPIZ and _GTK messages for now}else{qCWarning(lcQpaXcb) <<"Unhandled client message: "<<connection()->atomName(event->type);}}voidQXcbWindow::handleConfigureNotifyEvent(const xcb_configure_notify_event_t *event){bool fromSendEvent = (event->response_type &0x80); QPoint pos(event->x, event->y);if(!QPlatformWindow::parent() && !fromSendEvent) {// Do not trust the position, query it instead.auto reply =Q_XCB_REPLY(xcb_translate_coordinates,xcb_connection(),xcb_window(),xcbScreen()->root(),0,0);if(reply) { pos.setX(reply->dst_x); pos.setY(reply->dst_y);}}const QRect actualGeometry =QRect(pos,QSize(event->width, event->height)); QPlatformScreen *newScreen =QPlatformWindow::parent() ?QPlatformWindow::parent()->screen() :screenForGeometry(actualGeometry);if(!newScreen)return;QWindowSystemInterface::handleGeometryChange(window(), actualGeometry);// QPlatformScreen::screen() is updated asynchronously, so we can't compare it// with the newScreen. Just send the WindowScreenChanged event and QGuiApplication// will make the comparison later.QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->screen());if(!qFuzzyCompare(QHighDpiScaling::factor(newScreen), m_sizeHintsScaleFactor))propagateSizeHints();// Send the synthetic expose event on resize only when the window is shrunk,// because the "XCB_GRAVITY_NORTH_WEST" flag doesn't send it automatically.if(!m_oldWindowSize.isEmpty()&& (actualGeometry.width() < m_oldWindowSize.width()|| actualGeometry.height() < m_oldWindowSize.height())) {QWindowSystemInterface::handleExposeEvent(window(),QRegion(0,0, actualGeometry.width(), actualGeometry.height()));} m_oldWindowSize = actualGeometry.size();if(connection()->hasXSync() && m_syncState == SyncReceived) m_syncState = SyncAndConfigureReceived; m_dirtyFrameMargins =true;}boolQXcbWindow::isExposed()const{return m_mapped;}boolQXcbWindow::isEmbedded()const{return m_embedded;} QPoint QXcbWindow::mapToGlobal(const QPoint &pos)const{if(!m_embedded)returnQPlatformWindow::mapToGlobal(pos); QPoint ret;auto reply =Q_XCB_REPLY(xcb_translate_coordinates,xcb_connection(),xcb_window(),xcbScreen()->root(), pos.x(), pos.y());if(reply) { ret.setX(reply->dst_x); ret.setY(reply->dst_y);}return ret;} QPoint QXcbWindow::mapFromGlobal(const QPoint &pos)const{if(!m_embedded)returnQPlatformWindow::mapFromGlobal(pos); QPoint ret;auto reply =Q_XCB_REPLY(xcb_translate_coordinates,xcb_connection(),xcbScreen()->root(),xcb_window(), pos.x(), pos.y());if(reply) { ret.setX(reply->dst_x); ret.setY(reply->dst_y);}return ret;}voidQXcbWindow::handleMapNotifyEvent(const xcb_map_notify_event_t *event){if(event->window == m_window) { m_mappedMutex.lock(); m_mapped =true;const bool deferredActivation = m_deferredActivation; m_mappedMutex.unlock();if(deferredActivation)requestActivateWindow();QWindowSystemInterface::handleExposeEvent(window(),QRect(QPoint(),geometry().size()));}}voidQXcbWindow::handleUnmapNotifyEvent(const xcb_unmap_notify_event_t *event){if(event->window == m_window) { m_mappedMutex.lock(); m_mapped =false; m_mappedMutex.unlock();QWindowSystemInterface::handleExposeEvent(window(),QRegion());}}voidQXcbWindow::handleButtonPressEvent(int event_x,int event_y,int root_x,int root_y,int detail,Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp,QEvent::Type type,Qt::MouseEventSource source){const bool isWheel = detail >=4&& detail <=7;if(!isWheel &&window() !=QGuiApplication::focusWindow()) { QWindow *w =static_cast<QWindowPrivate *>(QObjectPrivate::get(window()))->eventReceiver();if(!(w->flags() & (Qt::WindowDoesNotAcceptFocus |Qt::BypassWindowManagerHint))&& w->type() !=Qt::ToolTip && w->type() !=Qt::Popup) { w->requestActivate();}}updateNetWmUserTime(timestamp);if(m_embedded && !m_trayIconWindow) {if(window() !=QGuiApplication::focusWindow()) {const QXcbWindow *container =static_cast<const QXcbWindow *>(QPlatformWindow::parent());Q_ASSERT(container !=nullptr);sendXEmbedMessage(container->xcb_window(), XEMBED_REQUEST_FOCUS);}} QPoint local(event_x, event_y); QPoint global(root_x, root_y);if(isWheel) {if(!connection()->isAtLeastXI21()) { QPoint angleDelta;if(detail ==4) angleDelta.setY(120);else if(detail ==5) angleDelta.setY(-120);else if(detail ==6) angleDelta.setX(120);else if(detail ==7) angleDelta.setX(-120);if(modifiers &Qt::AltModifier) angleDelta = angleDelta.transposed();QWindowSystemInterface::handleWheelEvent(window(), timestamp, local, global,QPoint(), angleDelta, modifiers);}return;}connection()->setMousePressWindow(this);handleMouseEvent(timestamp, local, global, modifiers, type, source);}voidQXcbWindow::handleButtonReleaseEvent(int event_x,int event_y,int root_x,int root_y,int detail,Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp,QEvent::Type type,Qt::MouseEventSource source){ QPoint local(event_x, event_y); QPoint global(root_x, root_y);if(detail >=4&& detail <=7) {// mouse wheel, handled in handleButtonPressEvent()return;}if(connection()->buttonState() ==Qt::NoButton) {connection()->setMousePressWindow(nullptr); m_ignorePressedWindowOnMouseLeave =false;}handleMouseEvent(timestamp, local, global, modifiers, type, source);}staticinlinebooldoCheckUnGrabAncestor(QXcbConnection *conn){/* Checking for XCB_NOTIFY_MODE_GRAB and XCB_NOTIFY_DETAIL_ANCESTOR prevents unwanted * enter/leave events on AwesomeWM on mouse button press. It also ignores duplicated * enter/leave events on Alt+Tab switching on some WMs with XInput2 events. * Without XInput2 events the (Un)grabAncestor cannot be checked when mouse button is * not pressed, otherwise (e.g. on Alt+Tab) it can igonre important enter/leave events. */if(conn) {const bool mouseButtonsPressed = (conn->buttonState() !=Qt::NoButton);return mouseButtonsPressed || conn->hasXInput2();}return true;}static boolignoreLeaveEvent(quint8 mode, quint8 detail, QXcbConnection *conn){return((doCheckUnGrabAncestor(conn)&& mode == XCB_NOTIFY_MODE_GRAB && detail == XCB_NOTIFY_DETAIL_ANCESTOR)|| (mode == XCB_NOTIFY_MODE_UNGRAB && detail == XCB_NOTIFY_DETAIL_INFERIOR)|| detail == XCB_NOTIFY_DETAIL_VIRTUAL || detail == XCB_NOTIFY_DETAIL_NONLINEAR_VIRTUAL);}static boolignoreEnterEvent(quint8 mode, quint8 detail, QXcbConnection *conn =nullptr){return((doCheckUnGrabAncestor(conn)&& mode == XCB_NOTIFY_MODE_UNGRAB && detail == XCB_NOTIFY_DETAIL_ANCESTOR)|| (mode != XCB_NOTIFY_MODE_NORMAL && mode != XCB_NOTIFY_MODE_UNGRAB)|| detail == XCB_NOTIFY_DETAIL_VIRTUAL || detail == XCB_NOTIFY_DETAIL_NONLINEAR_VIRTUAL);}voidQXcbWindow::handleEnterNotifyEvent(int event_x,int event_y,int root_x,int root_y, quint8 mode, quint8 detail, xcb_timestamp_t timestamp){connection()->setTime(timestamp);if(ignoreEnterEvent(mode, detail,connection())|| (connection()->mousePressWindow() && !m_ignorePressedWindowOnMouseLeave)) {return;}// Updates scroll valuators, as user might have done some scrolling outside our X client.connection()->xi2UpdateScrollingDevices();if(mode == XCB_NOTIFY_MODE_UNGRAB &&connection()->queryMouseButtons() !=Qt::NoButton) m_ignorePressedWindowOnMouseLeave =true;const QPoint global =QPoint(root_x, root_y);const QPoint local(event_x, event_y);QWindowSystemInterface::handleEnterEvent(window(), local, global);}voidQXcbWindow::handleLeaveNotifyEvent(int root_x,int root_y, quint8 mode, quint8 detail, xcb_timestamp_t timestamp){connection()->setTime(timestamp); QXcbWindow *mousePressWindow =connection()->mousePressWindow();if(ignoreLeaveEvent(mode, detail,connection())|| (mousePressWindow && !m_ignorePressedWindowOnMouseLeave)) {return;}// check if enter event is bufferedauto event =connection()->eventQueue()->peek([](xcb_generic_event_t *event,int type) {if(type != XCB_ENTER_NOTIFY)return false;auto enter =reinterpret_cast<xcb_enter_notify_event_t *>(event);return!ignoreEnterEvent(enter->mode, enter->detail);});auto enter =reinterpret_cast<xcb_enter_notify_event_t *>(event); QXcbWindow *enterWindow = enter ?connection()->platformWindowFromId(enter->event) :nullptr;if(enterWindow) { QPoint local(enter->event_x, enter->event_y); QPoint global =QPoint(root_x, root_y);QWindowSystemInterface::handleEnterLeaveEvent(enterWindow->window(),window(), local, global);}else{QWindowSystemInterface::handleLeaveEvent(window());if(m_ignorePressedWindowOnMouseLeave)connection()->setMousePressWindow(nullptr);}free(enter);}voidQXcbWindow::handleMotionNotifyEvent(int event_x,int event_y,int root_x,int root_y,Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp,QEvent::Type type,Qt::MouseEventSource source){ QPoint local(event_x, event_y); QPoint global(root_x, root_y);// "mousePressWindow" can be NULL i.e. if a window will be grabbed or unmapped, so set it again here.// Unset "mousePressWindow" when mouse button isn't pressed - in some cases the release event won't arrive.const bool isMouseButtonPressed = (connection()->buttonState() !=Qt::NoButton);const bool hasMousePressWindow = (connection()->mousePressWindow() !=nullptr);if(isMouseButtonPressed && !hasMousePressWindow)connection()->setMousePressWindow(this);else if(hasMousePressWindow && !isMouseButtonPressed)connection()->setMousePressWindow(nullptr);handleMouseEvent(timestamp, local, global, modifiers, type, source);}voidQXcbWindow::handleButtonPressEvent(const xcb_button_press_event_t *event){Qt::KeyboardModifiers modifiers =connection()->keyboard()->translateModifiers(event->state);handleButtonPressEvent(event->event_x, event->event_y, event->root_x, event->root_y, event->detail, modifiers, event->time,QEvent::MouseButtonPress);}voidQXcbWindow::handleButtonReleaseEvent(const xcb_button_release_event_t *event){Qt::KeyboardModifiers modifiers =connection()->keyboard()->translateModifiers(event->state);handleButtonReleaseEvent(event->event_x, event->event_y, event->root_x, event->root_y, event->detail, modifiers, event->time,QEvent::MouseButtonRelease);}voidQXcbWindow::handleMotionNotifyEvent(const xcb_motion_notify_event_t *event){Qt::KeyboardModifiers modifiers =connection()->keyboard()->translateModifiers(event->state);handleMotionNotifyEvent(event->event_x, event->event_y, event->root_x, event->root_y, modifiers, event->time,QEvent::MouseMove);}staticinlineintfixed1616ToInt(xcb_input_fp1616_t val){returnint(qreal(val) /0x10000);}#define qt_xcb_mask_is_set(ptr, event) (((unsigned char*)(ptr))[(event)>>3] & (1 << ((event) & 7)))voidQXcbWindow::handleXIMouseEvent(xcb_ge_event_t *event,Qt::MouseEventSource source){ QXcbConnection *conn =connection();auto*ev =reinterpret_cast<xcb_input_button_press_event_t *>(event);if(ev->buttons_len >0) {unsigned char*buttonMask = (unsigned char*) &ev[1];// There is a bug in the evdev driver which leads to receiving mouse events without// XIPointerEmulated being set: https://bugs.freedesktop.org/show_bug.cgi?id=98188// Filter them out by other attributes: when their source device is a touch screen// and the LMB is pressed.if(qt_xcb_mask_is_set(buttonMask,1) && conn->isTouchScreen(ev->sourceid)) {if(Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))qCDebug(lcQpaXInput,"XI2 mouse event from touch device %d was ignored", ev->sourceid);return;}for(int i =1; i <=15; ++i) conn->setButtonState(conn->translateMouseButton(i),qt_xcb_mask_is_set(buttonMask, i));}constQt::KeyboardModifiers modifiers = conn->keyboard()->translateModifiers(ev->mods.effective);const int event_x =fixed1616ToInt(ev->event_x);const int event_y =fixed1616ToInt(ev->event_y);const int root_x =fixed1616ToInt(ev->root_x);const int root_y =fixed1616ToInt(ev->root_y); conn->keyboard()->updateXKBStateFromXI(&ev->mods, &ev->group);constQt::MouseButton button = conn->xiToQtMouseButton(ev->detail);const char*sourceName =nullptr;if(Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled())) {const QMetaObject *metaObject =qt_getEnumMetaObject(source);const QMetaEnum me = metaObject->enumerator(metaObject->indexOfEnumerator(qt_getEnumName(source))); sourceName = me.valueToKey(source);}switch(ev->event_type) {case XCB_INPUT_BUTTON_PRESS:if(Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))qCDebug(lcQpaXInputEvents,"XI2 mouse press, button %d, time %d, source %s", button, ev->time, sourceName); conn->setButtonState(button,true);handleButtonPressEvent(event_x, event_y, root_x, root_y, ev->detail, modifiers, ev->time,QEvent::MouseButtonPress, source);break;case XCB_INPUT_BUTTON_RELEASE:if(Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))qCDebug(lcQpaXInputEvents,"XI2 mouse release, button %d, time %d, source %s", button, ev->time, sourceName); conn->setButtonState(button,false);handleButtonReleaseEvent(event_x, event_y, root_x, root_y, ev->detail, modifiers, ev->time,QEvent::MouseButtonRelease, source);break;case XCB_INPUT_MOTION:if(Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))qCDebug(lcQpaXInputEvents,"XI2 mouse motion %d,%d, time %d, source %s", event_x, event_y, ev->time, sourceName);handleMotionNotifyEvent(event_x, event_y, root_x, root_y, modifiers, ev->time,QEvent::MouseMove, source);break;default:qWarning() <<"Unrecognized XI2 mouse event"<< ev->event_type;break;}}voidQXcbWindow::handleXIEnterLeave(xcb_ge_event_t *event){auto*ev =reinterpret_cast<xcb_input_enter_event_t *>(event);// Compare the window with current mouse grabber to prevent deliver events to any other windows.// If leave event occurs and the window is under mouse - allow to deliver the leave event. QXcbWindow *mouseGrabber =connection()->mouseGrabber();if(mouseGrabber && mouseGrabber !=this&& (ev->event_type != XCB_INPUT_LEAVE ||QGuiApplicationPrivate::currentMouseWindow !=window())) {return;}const int root_x =fixed1616ToInt(ev->root_x);const int root_y =fixed1616ToInt(ev->root_y);switch(ev->event_type) {case XCB_INPUT_ENTER: {const int event_x =fixed1616ToInt(ev->event_x);const int event_y =fixed1616ToInt(ev->event_y);qCDebug(lcQpaXInputEvents,"XI2 mouse enter %d,%d, mode %d, detail %d, time %d", event_x, event_y, ev->mode, ev->detail, ev->time);handleEnterNotifyEvent(event_x, event_y, root_x, root_y, ev->mode, ev->detail, ev->time);break;}case XCB_INPUT_LEAVE:qCDebug(lcQpaXInputEvents,"XI2 mouse leave, mode %d, detail %d, time %d", ev->mode, ev->detail, ev->time);connection()->keyboard()->updateXKBStateFromXI(&ev->mods, &ev->group);handleLeaveNotifyEvent(root_x, root_y, ev->mode, ev->detail, ev->time);break;}} QXcbWindow *QXcbWindow::toWindow() {return this; }voidQXcbWindow::handleMouseEvent(xcb_timestamp_t time,const QPoint &local,const QPoint &global,Qt::KeyboardModifiers modifiers,QEvent::Type type,Qt::MouseEventSource source){ m_lastPointerPosition = local; m_lastPointerGlobalPosition = global;connection()->setTime(time);Qt::MouseButton button = type ==QEvent::MouseMove ?Qt::NoButton :connection()->button();QWindowSystemInterface::handleMouseEvent(window(), time, local, global,connection()->buttonState(), button, type, modifiers, source);}voidQXcbWindow::handleEnterNotifyEvent(const xcb_enter_notify_event_t *event){handleEnterNotifyEvent(event->event_x, event->event_y, event->root_x, event->root_y, event->mode, event->detail, event->time);}voidQXcbWindow::handleLeaveNotifyEvent(const xcb_leave_notify_event_t *event){handleLeaveNotifyEvent(event->root_x, event->root_y, event->mode, event->detail, event->time);}voidQXcbWindow::handlePropertyNotifyEvent(const xcb_property_notify_event_t *event){connection()->setTime(event->time);const bool propertyDeleted = event->state == XCB_PROPERTY_DELETE;if(event->atom ==atom(QXcbAtom::Atom_NET_WM_STATE) || event->atom ==atom(QXcbAtom::AtomWM_STATE)) {if(propertyDeleted)return;Qt::WindowStates newState =Qt::WindowNoState;if(event->atom ==atom(QXcbAtom::AtomWM_STATE)) {// WM_STATE: Quick check for 'Minimize'.auto reply =Q_XCB_REPLY(xcb_get_property,xcb_connection(),0, m_window,atom(QXcbAtom::AtomWM_STATE), XCB_ATOM_ANY,0,1024);if(reply && reply->format ==32&& reply->type ==atom(QXcbAtom::AtomWM_STATE)) {const quint32 *data = (const quint32 *)xcb_get_property_value(reply.get());if(reply->length !=0) m_minimized = (data[0] == XCB_ICCCM_WM_STATE_ICONIC || (data[0] == XCB_ICCCM_WM_STATE_WITHDRAWN && m_minimized));}}const NetWmStates states =netWmStates();// _NET_WM_STATE_HIDDEN should be set by the Window Manager to indicate that a window would// not be visible on the screen if its desktop/viewport were active and its coordinates were// within the screen bounds. The canonical example is that minimized windows should be in// the _NET_WM_STATE_HIDDEN state.if(m_minimized && (!connection()->wmSupport()->isSupportedByWM(NetWmStateHidden)|| states.testFlag(NetWmStateHidden))) newState =Qt::WindowMinimized;if(states & NetWmStateFullScreen) newState |=Qt::WindowFullScreen;if((states & NetWmStateMaximizedHorz) && (states & NetWmStateMaximizedVert)) newState |=Qt::WindowMaximized;// Send Window state, compress events in case other flags (modality, etc) are changed.if(m_lastWindowStateEvent != newState) {QWindowSystemInterface::handleWindowStateChanged(window(), newState); m_lastWindowStateEvent = newState; m_windowState = newState;if((m_windowState &Qt::WindowMinimized) &&connection()->mouseGrabber() ==this)connection()->setMouseGrabber(nullptr);}return;}else if(event->atom ==atom(QXcbAtom::Atom_NET_FRAME_EXTENTS)) { m_dirtyFrameMargins =true;}}voidQXcbWindow::handleFocusInEvent(const xcb_focus_in_event_t *event){// Ignore focus events that are being sent only because the pointer is over// our window, even if the input focus is in a different window.if(event->detail == XCB_NOTIFY_DETAIL_POINTER)return;connection()->focusInTimer().stop();doFocusIn();}voidQXcbWindow::handleFocusOutEvent(const xcb_focus_out_event_t *event){// Ignore focus events that are being sent only because the pointer is over// our window, even if the input focus is in a different window.if(event->detail == XCB_NOTIFY_DETAIL_POINTER)return;doFocusOut();}voidQXcbWindow::updateSyncRequestCounter(){if(m_syncState != SyncAndConfigureReceived) {// window manager does not expect a sync event yet.return;}if(connection()->hasXSync() && (m_syncValue.lo !=0|| m_syncValue.hi !=0)) {xcb_sync_set_counter(xcb_connection(), m_syncCounter, m_syncValue);xcb_flush(xcb_connection()); m_syncValue.lo =0; m_syncValue.hi =0; m_syncState = NoSyncNeeded;}}const xcb_visualtype_t *QXcbWindow::createVisual(){returnxcbScreen() ?xcbScreen()->visualForFormat(m_format):nullptr;}boolQXcbWindow::setKeyboardGrabEnabled(bool grab){if(grab && !connection()->canGrab())return false;if(!grab) {xcb_ungrab_keyboard(xcb_connection(), XCB_TIME_CURRENT_TIME);return true;}auto reply =Q_XCB_REPLY(xcb_grab_keyboard,xcb_connection(),false, m_window, XCB_TIME_CURRENT_TIME, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);return reply && reply->status == XCB_GRAB_STATUS_SUCCESS;}boolQXcbWindow::setMouseGrabEnabled(bool grab){if(!grab &&connection()->mouseGrabber() ==this)connection()->setMouseGrabber(nullptr);if(grab && !connection()->canGrab())return false;if(connection()->hasXInput2()) {bool result =connection()->xi2SetMouseGrabEnabled(m_window, grab);if(grab && result)connection()->setMouseGrabber(this);return result;}if(!grab) {xcb_ungrab_pointer(xcb_connection(), XCB_TIME_CURRENT_TIME);return true;}auto reply =Q_XCB_REPLY(xcb_grab_pointer,xcb_connection(),false, m_window,(XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_BUTTON_MOTION | XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW | XCB_EVENT_MASK_POINTER_MOTION), XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, XCB_WINDOW_NONE, XCB_CURSOR_NONE, XCB_TIME_CURRENT_TIME);bool result = reply && reply->status == XCB_GRAB_STATUS_SUCCESS;if(result)connection()->setMouseGrabber(this);return result;}boolQXcbWindow::windowEvent(QEvent *event){switch(event->type()) {caseQEvent::FocusIn:if(m_embedded && !m_trayIconWindow && !event->spontaneous()) { QFocusEvent *focusEvent =static_cast<QFocusEvent *>(event);switch(focusEvent->reason()) {caseQt::TabFocusReason:caseQt::BacktabFocusReason:{const QXcbWindow *container =static_cast<const QXcbWindow *>(QPlatformWindow::parent());sendXEmbedMessage(container->xcb_window(), focusEvent->reason() ==Qt::TabFocusReason ? XEMBED_FOCUS_NEXT : XEMBED_FOCUS_PREV); event->accept();}break;default:break;}}break;default:break;}returnQPlatformWindow::windowEvent(event);}boolQXcbWindow::startSystemResize(Qt::Edges edges){returnstartSystemMoveResize(m_lastPointerPosition, edges);}boolQXcbWindow::startSystemMove(){returnstartSystemMoveResize(m_lastPointerPosition,16);}boolQXcbWindow::startSystemMoveResize(const QPoint &pos,int edges){const xcb_atom_t moveResize =connection()->atom(QXcbAtom::Atom_NET_WM_MOVERESIZE);if(!connection()->wmSupport()->isSupportedByWM(moveResize))return false;// ### FIXME QTBUG-53389bool startedByTouch =connection()->startSystemMoveResizeForTouch(m_window, edges);if(startedByTouch) {const QString wmname =connection()->windowManagerName();if(wmname !="kwin"_L1 && wmname !="openbox"_L1) {qCDebug(lcQpaInputDevices) <<"only KDE and OpenBox support startSystemMove/Resize which is triggered from touch events: XDG_CURRENT_DESKTOP="<<qgetenv("XDG_CURRENT_DESKTOP");connection()->abortSystemMoveResize(m_window);return false;}// KWin, Openbox, AwesomeWM and Gnome have been tested to work with _NET_WM_MOVERESIZE.}else{// Started by mouse press.doStartSystemMoveResize(mapToGlobal(pos), edges);}return true;}static uint qtEdgesToXcbMoveResizeDirection(Qt::Edges edges){if(edges == (Qt::TopEdge |Qt::LeftEdge))return0;if(edges ==Qt::TopEdge)return1;if(edges == (Qt::TopEdge |Qt::RightEdge))return2;if(edges ==Qt::RightEdge)return3;if(edges == (Qt::RightEdge |Qt::BottomEdge))return4;if(edges ==Qt::BottomEdge)return5;if(edges == (Qt::BottomEdge |Qt::LeftEdge))return6;if(edges ==Qt::LeftEdge)return7;qWarning() <<"Cannot convert "<< edges <<"to _NET_WM_MOVERESIZE direction.";return0;}voidQXcbWindow::doStartSystemMoveResize(const QPoint &globalPos,int edges){qCDebug(lcQpaInputDevices) <<"triggered system move or resize via sending _NET_WM_MOVERESIZE client message";const xcb_atom_t moveResize =connection()->atom(QXcbAtom::Atom_NET_WM_MOVERESIZE); xcb_client_message_event_t xev; xev.response_type = XCB_CLIENT_MESSAGE; xev.type = moveResize; xev.sequence =0; xev.window =xcb_window(); xev.format =32; xev.data.data32[0] = globalPos.x(); xev.data.data32[1] = globalPos.y();if(edges ==16) xev.data.data32[2] =8;// moveelse xev.data.data32[2] =qtEdgesToXcbMoveResizeDirection(Qt::Edges(edges)); xev.data.data32[3] = XCB_BUTTON_INDEX_1; xev.data.data32[4] =0;xcb_ungrab_pointer(connection()->xcb_connection(), XCB_CURRENT_TIME);xcb_send_event(connection()->xcb_connection(),false,xcbScreen()->root(), XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY,(const char*)&xev);connection()->setDuringSystemMoveResize(true);}// Sends an XEmbed message.voidQXcbWindow::sendXEmbedMessage(xcb_window_t window, quint32 message, quint32 detail, quint32 data1, quint32 data2){ xcb_client_message_event_t event; event.response_type = XCB_CLIENT_MESSAGE; event.format =32; event.sequence =0; event.window = window; event.type =atom(QXcbAtom::Atom_XEMBED); event.data.data32[0] =connection()->time(); event.data.data32[1] = message; event.data.data32[2] = detail; event.data.data32[3] = data1; event.data.data32[4] = data2;xcb_send_event(xcb_connection(),false, window, XCB_EVENT_MASK_NO_EVENT, (const char*)&event);}static boolfocusWindowChangeQueued(const QWindow *window){/* Check from window system event queue if the next queued activation * targets a window other than @window. */QWindowSystemInterfacePrivate::FocusWindowEvent *systemEvent =static_cast<QWindowSystemInterfacePrivate::FocusWindowEvent *>(QWindowSystemInterfacePrivate::peekWindowSystemEvent(QWindowSystemInterfacePrivate::FocusWindow));return systemEvent && systemEvent->focused != window;}voidQXcbWindow::handleXEmbedMessage(const xcb_client_message_event_t *event){connection()->setTime(event->data.data32[0]);switch(event->data.data32[1]) {case XEMBED_WINDOW_ACTIVATE:case XEMBED_WINDOW_DEACTIVATE:break;case XEMBED_EMBEDDED_NOTIFY:xcb_map_window(xcb_connection(), m_window);xcbScreen()->windowShown(this);break;case XEMBED_FOCUS_IN:connection()->focusInTimer().stop();Qt::FocusReason reason;switch(event->data.data32[2]) {case XEMBED_FOCUS_FIRST: reason =Qt::TabFocusReason;break;case XEMBED_FOCUS_LAST: reason =Qt::BacktabFocusReason;break;case XEMBED_FOCUS_CURRENT:default: reason =Qt::OtherFocusReason;break;}connection()->setFocusWindow(window());QWindowSystemInterface::handleFocusWindowChanged(window(), reason);break;case XEMBED_FOCUS_OUT:if(window() ==QGuiApplication::focusWindow()&& !focusWindowChangeQueued(window())) {connection()->setFocusWindow(nullptr);QWindowSystemInterface::handleFocusWindowChanged(nullptr);}break;}}staticinline xcb_rectangle_t qRectToXCBRectangle(const QRect &r){ xcb_rectangle_t result; result.x =qMax(SHRT_MIN, r.x()); result.y =qMax(SHRT_MIN, r.y()); result.width =qMin((int)USHRT_MAX, r.width()); result.height =qMin((int)USHRT_MAX, r.height());return result;}voidQXcbWindow::setOpacity(qreal level){if(!m_window)return; quint32 value =qRound64(qBound(qreal(0), level,qreal(1)) *0xffffffff);xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,atom(QXcbAtom::Atom_NET_WM_WINDOW_OPACITY), XCB_ATOM_CARDINAL,32,1,(uchar *)&value);} QList<xcb_rectangle_t>qRegionToXcbRectangleList(const QRegion &region){ QList<xcb_rectangle_t> rects; rects.reserve(region.rectCount());for(const QRect &r : region) rects.push_back(qRectToXCBRectangle(r));return rects;}voidQXcbWindow::setMask(const QRegion &region){if(!connection()->hasXShape())return;if(region.isEmpty()) {xcb_shape_mask(connection()->xcb_connection(), XCB_SHAPE_SO_SET, XCB_SHAPE_SK_BOUNDING,xcb_window(),0,0, XCB_NONE);}else{constauto rects =qRegionToXcbRectangleList(region);xcb_shape_rectangles(connection()->xcb_connection(), XCB_SHAPE_SO_SET, XCB_SHAPE_SK_BOUNDING, XCB_CLIP_ORDERING_UNSORTED,xcb_window(),0,0, rects.size(), &rects[0]);}}voidQXcbWindow::setAlertState(bool enabled){if(m_alertState == enabled)return; m_alertState = enabled;setNetWmState(enabled,atom(QXcbAtom::Atom_NET_WM_STATE_DEMANDS_ATTENTION));} uint QXcbWindow::visualId()const{return m_visualId;}boolQXcbWindow::needsSync()const{return m_syncState == SyncAndConfigureReceived;}voidQXcbWindow::postSyncWindowRequest(){if(!m_pendingSyncRequest) { QXcbSyncWindowRequest *e =newQXcbSyncWindowRequest(this); m_pendingSyncRequest = e;QCoreApplication::postEvent(xcbScreen()->connection(), e);}} QXcbScreen *QXcbWindow::xcbScreen()const{return static_cast<QXcbScreen *>(screen());}voidQXcbWindow::setWindowTitle(const QXcbConnection *conn, xcb_window_t window,const QString &title){ QString fullTitle =formatWindowTitle(title,QString::fromUtf8("\xe2\x80\x94"));// unicode character U+2014, EM DASHconst QByteArray ba =std::move(fullTitle).toUtf8();xcb_change_property(conn->xcb_connection(), XCB_PROP_MODE_REPLACE, window, conn->atom(QXcbAtom::Atom_NET_WM_NAME), conn->atom(QXcbAtom::AtomUTF8_STRING),8, ba.size(), ba.constData());#if QT_CONFIG(xcb_xlib) Display *dpy =static_cast<Display *>(conn->xlib_display()); XTextProperty *text =qstringToXTP(dpy, title);if(text)XSetWMName(dpy, window, text);#endifxcb_flush(conn->xcb_connection());} QString QXcbWindow::windowTitle(const QXcbConnection *conn, xcb_window_t window){const xcb_atom_t utf8Atom = conn->atom(QXcbAtom::AtomUTF8_STRING);auto reply =Q_XCB_REPLY_UNCHECKED(xcb_get_property, conn->xcb_connection(),false, window, conn->atom(QXcbAtom::Atom_NET_WM_NAME), utf8Atom,0,1024);if(reply && reply->format ==8&& reply->type == utf8Atom) {const char*name =reinterpret_cast<const char*>(xcb_get_property_value(reply.get()));returnQString::fromUtf8(name,xcb_get_property_value_length(reply.get()));} reply =Q_XCB_REPLY_UNCHECKED(xcb_get_property, conn->xcb_connection(),false, window, conn->atom(QXcbAtom::AtomWM_NAME), XCB_ATOM_STRING,0,1024);if(reply && reply->format ==8&& reply->type == XCB_ATOM_STRING) {const char*name =reinterpret_cast<const char*>(xcb_get_property_value(reply.get()));returnQString::fromLatin1(name,xcb_get_property_value_length(reply.get()));}returnQString();} QT_END_NAMESPACE 
close