Warning, file /plasma/kwin/src/main_x11.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 KWin - the KDE window manager 0003 This file is part of the KDE project. 0004 0005 SPDX-FileCopyrightText: 1999, 2000 Matthias Ettrich <ettrich@kde.org> 0006 SPDX-FileCopyrightText: 2003 Lubos Lunak <l.lunak@kde.org> 0007 SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org> 0008 0009 SPDX-License-Identifier: GPL-2.0-or-later 0010 */ 0011 #include "main_x11.h" 0012 0013 #include <config-kwin.h> 0014 0015 #include "backends/x11/standalone/x11_standalone_backend.h" 0016 #include "core/outputbackend.h" 0017 #include "core/session.h" 0018 #include "outline.h" 0019 #include "screenedge.h" 0020 #include "sm.h" 0021 #include "tabletmodemanager.h" 0022 #include "utils/xcbutils.h" 0023 #include "workspace.h" 0024 0025 #include <KConfigGroup> 0026 #include <KCrash> 0027 #include <KGlobalAccel> 0028 #include <KLocalizedString> 0029 #include <KSelectionOwner> 0030 #include <KSignalHandler> 0031 0032 #include <QAction> 0033 #include <QComboBox> 0034 #include <QCommandLineParser> 0035 #include <QDialog> 0036 #include <QDialogButtonBox> 0037 #include <QFile> 0038 #include <QLabel> 0039 #include <QPushButton> 0040 #include <QSurfaceFormat> 0041 #include <QVBoxLayout> 0042 #include <qplatformdefs.h> 0043 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 0044 #include <private/qtx11extras_p.h> 0045 #else 0046 #include <QX11Info> 0047 #endif 0048 #include <QtDBus> 0049 0050 // system 0051 #include <iostream> 0052 #include <unistd.h> 0053 0054 Q_LOGGING_CATEGORY(KWIN_CORE, "kwin_core", QtWarningMsg) 0055 0056 namespace KWin 0057 { 0058 0059 class AlternativeWMDialog : public QDialog 0060 { 0061 public: 0062 AlternativeWMDialog() 0063 : QDialog() 0064 { 0065 QWidget *mainWidget = new QWidget(this); 0066 QVBoxLayout *layout = new QVBoxLayout(mainWidget); 0067 QString text = i18n("KWin is unstable.\n" 0068 "It seems to have crashed several times in a row.\n" 0069 "You can select another window manager to run:"); 0070 QLabel *textLabel = new QLabel(text, mainWidget); 0071 layout->addWidget(textLabel); 0072 wmList = new QComboBox(mainWidget); 0073 wmList->setEditable(true); 0074 layout->addWidget(wmList); 0075 0076 addWM(QStringLiteral("metacity")); 0077 addWM(QStringLiteral("openbox")); 0078 addWM(QStringLiteral("fvwm2")); 0079 addWM(QStringLiteral("kwin_x11")); 0080 0081 QVBoxLayout *mainLayout = new QVBoxLayout(this); 0082 mainLayout->addWidget(mainWidget); 0083 QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); 0084 buttons->button(QDialogButtonBox::Ok)->setDefault(true); 0085 connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept); 0086 connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); 0087 mainLayout->addWidget(buttons); 0088 0089 raise(); 0090 } 0091 0092 void addWM(const QString &wm) 0093 { 0094 // TODO: Check if WM is installed 0095 if (!QStandardPaths::findExecutable(wm).isEmpty()) { 0096 wmList->addItem(wm); 0097 } 0098 } 0099 QString selectedWM() const 0100 { 0101 return wmList->currentText(); 0102 } 0103 0104 private: 0105 QComboBox *wmList; 0106 }; 0107 0108 class KWinSelectionOwner : public KSelectionOwner 0109 { 0110 Q_OBJECT 0111 public: 0112 explicit KWinSelectionOwner() 0113 : KSelectionOwner(make_selection_atom()) 0114 { 0115 } 0116 0117 private: 0118 bool genericReply(xcb_atom_t target_P, xcb_atom_t property_P, xcb_window_t requestor_P) override 0119 { 0120 if (target_P == xa_version) { 0121 int32_t version[] = {2, 0}; 0122 xcb_change_property(kwinApp()->x11Connection(), XCB_PROP_MODE_REPLACE, requestor_P, 0123 property_P, XCB_ATOM_INTEGER, 32, 2, version); 0124 } else { 0125 return KSelectionOwner::genericReply(target_P, property_P, requestor_P); 0126 } 0127 return true; 0128 } 0129 0130 void replyTargets(xcb_atom_t property_P, xcb_window_t requestor_P) override 0131 { 0132 KSelectionOwner::replyTargets(property_P, requestor_P); 0133 xcb_atom_t atoms[1] = {xa_version}; 0134 // PropModeAppend ! 0135 xcb_change_property(kwinApp()->x11Connection(), XCB_PROP_MODE_APPEND, requestor_P, 0136 property_P, XCB_ATOM_ATOM, 32, 1, atoms); 0137 } 0138 0139 void getAtoms() override 0140 { 0141 KSelectionOwner::getAtoms(); 0142 if (xa_version == XCB_ATOM_NONE) { 0143 const QByteArray name(QByteArrayLiteral("VERSION")); 0144 UniqueCPtr<xcb_intern_atom_reply_t> atom(xcb_intern_atom_reply( 0145 kwinApp()->x11Connection(), 0146 xcb_intern_atom_unchecked(kwinApp()->x11Connection(), false, name.length(), name.constData()), 0147 nullptr)); 0148 if (atom) { 0149 xa_version = atom->atom; 0150 } 0151 } 0152 } 0153 0154 xcb_atom_t make_selection_atom() 0155 { 0156 QByteArray screen(QByteArrayLiteral("WM_S0")); 0157 UniqueCPtr<xcb_intern_atom_reply_t> atom(xcb_intern_atom_reply( 0158 kwinApp()->x11Connection(), 0159 xcb_intern_atom_unchecked(kwinApp()->x11Connection(), false, screen.length(), screen.constData()), 0160 nullptr)); 0161 if (!atom) { 0162 return XCB_ATOM_NONE; 0163 } 0164 return atom->atom; 0165 } 0166 static xcb_atom_t xa_version; 0167 }; 0168 xcb_atom_t KWinSelectionOwner::xa_version = XCB_ATOM_NONE; 0169 0170 //************************************ 0171 // ApplicationX11 0172 //************************************ 0173 0174 ApplicationX11::ApplicationX11(int &argc, char **argv) 0175 : Application(OperationModeX11, argc, argv) 0176 , owner() 0177 , m_replace(false) 0178 { 0179 setX11Connection(QX11Info::connection()); 0180 setX11RootWindow(QX11Info::appRootWindow()); 0181 } 0182 0183 ApplicationX11::~ApplicationX11() 0184 { 0185 setTerminating(); 0186 destroyPlugins(); 0187 destroyCompositor(); 0188 destroyColorManager(); 0189 destroyWorkspace(); 0190 // If there was no --replace (no new WM) 0191 if (owner != nullptr && owner->ownerWindow() != XCB_WINDOW_NONE) { 0192 Xcb::setInputFocus(XCB_INPUT_FOCUS_POINTER_ROOT); 0193 } 0194 } 0195 0196 void ApplicationX11::setReplace(bool replace) 0197 { 0198 m_replace = replace; 0199 } 0200 0201 std::unique_ptr<Edge> ApplicationX11::createScreenEdge(ScreenEdges *parent) 0202 { 0203 return static_cast<X11StandaloneBackend *>(outputBackend())->createScreenEdge(parent); 0204 } 0205 0206 void ApplicationX11::createPlatformCursor(QObject *parent) 0207 { 0208 static_cast<X11StandaloneBackend *>(outputBackend())->createPlatformCursor(parent); 0209 } 0210 0211 std::unique_ptr<OutlineVisual> ApplicationX11::createOutline(Outline *outline) 0212 { 0213 // first try composited Outline 0214 if (auto outlineVisual = Application::createOutline(outline)) { 0215 return outlineVisual; 0216 } 0217 return static_cast<X11StandaloneBackend *>(outputBackend())->createOutline(outline); 0218 } 0219 0220 void ApplicationX11::createEffectsHandler(Compositor *compositor, WorkspaceScene *scene) 0221 { 0222 static_cast<X11StandaloneBackend *>(outputBackend())->createEffectsHandler(compositor, scene); 0223 } 0224 0225 void ApplicationX11::startInteractiveWindowSelection(std::function<void(KWin::Window *)> callback, const QByteArray &cursorName) 0226 { 0227 static_cast<X11StandaloneBackend *>(outputBackend())->startInteractiveWindowSelection(callback, cursorName); 0228 } 0229 0230 void ApplicationX11::startInteractivePositionSelection(std::function<void(const QPoint &)> callback) 0231 { 0232 static_cast<X11StandaloneBackend *>(outputBackend())->startInteractivePositionSelection(callback); 0233 } 0234 0235 PlatformCursorImage ApplicationX11::cursorImage() const 0236 { 0237 return static_cast<X11StandaloneBackend *>(outputBackend())->cursorImage(); 0238 } 0239 0240 void ApplicationX11::lostSelection() 0241 { 0242 sendPostedEvents(); 0243 destroyPlugins(); 0244 destroyCompositor(); 0245 destroyColorManager(); 0246 destroyWorkspace(); 0247 // Remove windowmanager privileges 0248 Xcb::selectInput(kwinApp()->x11RootWindow(), XCB_EVENT_MASK_PROPERTY_CHANGE); 0249 removeNativeX11EventFilter(); 0250 quit(); 0251 } 0252 0253 void ApplicationX11::performStartup() 0254 { 0255 crashChecking(); 0256 0257 owner.reset(new KWinSelectionOwner()); 0258 connect(owner.get(), &KSelectionOwner::failedToClaimOwnership, [] { 0259 fputs(i18n("kwin: unable to claim manager selection, another wm running? (try using --replace)\n").toLocal8Bit().constData(), stderr); 0260 ::exit(1); 0261 }); 0262 connect(owner.get(), &KSelectionOwner::lostOwnership, this, &ApplicationX11::lostSelection); 0263 connect(owner.get(), &KSelectionOwner::claimedOwnership, this, [this] { 0264 installNativeX11EventFilter(); 0265 // first load options - done internally by a different thread 0266 createOptions(); 0267 0268 if (!outputBackend()->initialize()) { 0269 std::exit(1); 0270 } 0271 0272 // Check whether another windowmanager is running 0273 const uint32_t maskValues[] = {XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT}; 0274 UniqueCPtr<xcb_generic_error_t> redirectCheck(xcb_request_check(kwinApp()->x11Connection(), 0275 xcb_change_window_attributes_checked(kwinApp()->x11Connection(), 0276 kwinApp()->x11RootWindow(), 0277 XCB_CW_EVENT_MASK, 0278 maskValues))); 0279 if (redirectCheck) { 0280 fputs(i18n("kwin: another window manager is running (try using --replace)\n").toLocal8Bit().constData(), stderr); 0281 if (!wasCrash()) { // if this is a crash-restart, DrKonqi may have stopped the process w/o killing the connection 0282 ::exit(1); 0283 } 0284 } 0285 0286 // Update the timestamp if a global shortcut is pressed or released. Needed 0287 // to ensure that kwin can grab the keyboard. 0288 connect(KGlobalAccel::self(), &KGlobalAccel::globalShortcutActiveChanged, this, [this](QAction *triggeredAction) { 0289 QVariant timestamp = triggeredAction->property("org.kde.kglobalaccel.activationTimestamp"); 0290 bool ok = false; 0291 const quint32 t = timestamp.toULongLong(&ok); 0292 if (ok) { 0293 kwinApp()->setX11Time(t); 0294 } 0295 }); 0296 0297 createInput(); 0298 createWorkspace(); 0299 createColorManager(); 0300 createPlugins(); 0301 0302 Xcb::sync(); // Trigger possible errors, there's still a chance to abort 0303 0304 notifyKSplash(); 0305 notifyStarted(); 0306 }); 0307 // we need to do an XSync here, otherwise the QPA might crash us later on 0308 Xcb::sync(); 0309 owner->claim(m_replace || wasCrash(), true); 0310 0311 createAtoms(); 0312 0313 createTabletModeManager(); 0314 } 0315 0316 void ApplicationX11::setupCrashHandler() 0317 { 0318 KCrash::setEmergencySaveFunction(ApplicationX11::crashHandler); 0319 } 0320 0321 void ApplicationX11::crashChecking() 0322 { 0323 setupCrashHandler(); 0324 if (crashes >= 4) { 0325 // Something has gone seriously wrong 0326 AlternativeWMDialog dialog; 0327 QString cmd = QStringLiteral("kwin_x11"); 0328 if (dialog.exec() == QDialog::Accepted) { 0329 cmd = dialog.selectedWM(); 0330 } else { 0331 ::exit(1); 0332 } 0333 if (cmd.length() > 500) { 0334 qCDebug(KWIN_CORE) << "Command is too long, truncating"; 0335 cmd = cmd.left(500); 0336 } 0337 qCDebug(KWIN_CORE) << "Starting" << cmd << "and exiting"; 0338 char buf[1024]; 0339 sprintf(buf, "%s &", cmd.toLatin1().data()); 0340 system(buf); 0341 ::exit(1); 0342 } 0343 // Reset crashes count if we stay up for more that 15 seconds 0344 QTimer::singleShot(15 * 1000, this, &Application::resetCrashesCount); 0345 } 0346 0347 void ApplicationX11::notifyKSplash() 0348 { 0349 // Tell KSplash that KWin has started 0350 QDBusMessage ksplashProgressMessage = QDBusMessage::createMethodCall(QStringLiteral("org.kde.KSplash"), 0351 QStringLiteral("/KSplash"), 0352 QStringLiteral("org.kde.KSplash"), 0353 QStringLiteral("setStage")); 0354 ksplashProgressMessage.setArguments(QList<QVariant>() << QStringLiteral("wm")); 0355 QDBusConnection::sessionBus().asyncCall(ksplashProgressMessage); 0356 } 0357 0358 void ApplicationX11::crashHandler(int signal) 0359 { 0360 crashes++; 0361 0362 fprintf(stderr, "Application::crashHandler() called with signal %d; recent crashes: %d\n", signal, crashes); 0363 char cmd[1024]; 0364 sprintf(cmd, "%s --crashes %d &", 0365 QFile::encodeName(QCoreApplication::applicationFilePath()).constData(), crashes); 0366 0367 sleep(1); 0368 system(cmd); 0369 } 0370 0371 } // namespace 0372 0373 int main(int argc, char *argv[]) 0374 { 0375 KWin::Application::setupMalloc(); 0376 KWin::Application::setupLocalizedString(); 0377 0378 signal(SIGPIPE, SIG_IGN); 0379 0380 // enforce xcb plugin, unfortunately command line switch has precedence 0381 setenv("QT_QPA_PLATFORM", "xcb", true); 0382 0383 qunsetenv("QT_DEVICE_PIXEL_RATIO"); 0384 qunsetenv("QT_SCALE_FACTOR"); 0385 QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling); 0386 // KSMServer talks to us directly on DBus. 0387 QCoreApplication::setAttribute(Qt::AA_DisableSessionManager); 0388 // For sharing thumbnails between our scene graph and qtquick. 0389 QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); 0390 0391 QSurfaceFormat format = QSurfaceFormat::defaultFormat(); 0392 // shared opengl contexts must have the same reset notification policy 0393 format.setOptions(QSurfaceFormat::ResetNotification); 0394 // disables vsync for any QtQuick windows we create (BUG 406180) 0395 format.setSwapInterval(0); 0396 QSurfaceFormat::setDefaultFormat(format); 0397 0398 KWin::ApplicationX11 a(argc, argv); 0399 a.setupTranslator(); 0400 // reset QT_QPA_PLATFORM so we don't propagate it to our children (e.g. apps launched from the overview effect) 0401 qunsetenv("QT_QPA_PLATFORM"); 0402 0403 KSignalHandler::self()->watchSignal(SIGTERM); 0404 KSignalHandler::self()->watchSignal(SIGINT); 0405 KSignalHandler::self()->watchSignal(SIGHUP); 0406 QObject::connect(KSignalHandler::self(), &KSignalHandler::signalReceived, 0407 &a, &QCoreApplication::exit); 0408 0409 KWin::Application::createAboutData(); 0410 0411 QCommandLineOption replaceOption(QStringLiteral("replace"), i18n("Replace already-running ICCCM2.0-compliant window manager")); 0412 0413 QCommandLineParser parser; 0414 a.setupCommandLine(&parser); 0415 parser.addOption(replaceOption); 0416 #if KWIN_BUILD_ACTIVITIES 0417 QCommandLineOption noActivitiesOption(QStringLiteral("no-kactivities"), 0418 i18n("Disable KActivities integration.")); 0419 parser.addOption(noActivitiesOption); 0420 #endif 0421 0422 parser.process(a); 0423 a.processCommandLine(&parser); 0424 a.setReplace(parser.isSet(replaceOption)); 0425 #if KWIN_BUILD_ACTIVITIES 0426 if (parser.isSet(noActivitiesOption)) { 0427 a.setUseKActivities(false); 0428 } 0429 #endif 0430 0431 // perform sanity checks 0432 if (a.platformName().toLower() != QStringLiteral("xcb")) { 0433 fprintf(stderr, "%s: FATAL ERROR expecting platform xcb but got platform %s\n", 0434 argv[0], qPrintable(a.platformName())); 0435 exit(1); 0436 } 0437 if (!QX11Info::display()) { 0438 fprintf(stderr, "%s: FATAL ERROR KWin requires Xlib support in the xcb plugin. Do not configure Qt with -no-xcb-xlib\n", 0439 argv[0]); 0440 exit(1); 0441 } 0442 0443 a.setSession(KWin::Session::create(KWin::Session::Type::Noop)); 0444 a.setOutputBackend(std::make_unique<KWin::X11StandaloneBackend>()); 0445 a.start(); 0446 0447 return a.exec(); 0448 } 0449 0450 #include "main_x11.moc"