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