File indexing completed on 2024-09-08 13:19:57
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 0008 SPDX-License-Identifier: GPL-2.0-or-later 0009 */ 0010 #include "main.h" 0011 0012 #include <config-kwin.h> 0013 0014 #include "atoms.h" 0015 #include "colormanager.h" 0016 #include "composite.h" 0017 #include "core/outputbackend.h" 0018 #include "core/session.h" 0019 #include "cursor.h" 0020 #include "effects.h" 0021 #include "input.h" 0022 #include "inputmethod.h" 0023 #include "options.h" 0024 #include "outline.h" 0025 #include "pluginmanager.h" 0026 #include "pointer_input.h" 0027 #include "screenedge.h" 0028 #include "sm.h" 0029 #include "tabletmodemanager.h" 0030 #include "utils/xcbutils.h" 0031 #include "wayland/surface_interface.h" 0032 #include "workspace.h" 0033 #include "x11eventfilter.h" 0034 0035 #if KWIN_BUILD_SCREENLOCKER 0036 #include "screenlockerwatcher.h" 0037 #endif 0038 0039 #include <kwineffects.h> 0040 0041 // KDE 0042 #include <KAboutData> 0043 #include <KLocalizedString> 0044 // Qt 0045 #include <QCommandLineParser> 0046 #include <QLibraryInfo> 0047 #include <QQuickWindow> 0048 #include <QStandardPaths> 0049 #include <QTranslator> 0050 #include <qplatformdefs.h> 0051 0052 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 0053 #include <private/qtx11extras_p.h> 0054 #else 0055 #include <QX11Info> 0056 #endif 0057 0058 #include <cerrno> 0059 0060 #if __has_include(<malloc.h>) 0061 #include <malloc.h> 0062 #endif 0063 #include <unistd.h> 0064 0065 // xcb 0066 #include <xcb/damage.h> 0067 #ifndef XCB_GE_GENERIC 0068 #define XCB_GE_GENERIC 35 0069 #endif 0070 0071 Q_DECLARE_METATYPE(KSharedConfigPtr) 0072 0073 namespace KWin 0074 { 0075 0076 Options *options; 0077 Atoms *atoms; 0078 int Application::crashes = 0; 0079 0080 Application::Application(Application::OperationMode mode, int &argc, char **argv) 0081 : QApplication(argc, argv) 0082 , m_eventFilter(new XcbEventFilter()) 0083 , m_configLock(false) 0084 , m_config(KSharedConfig::openConfig(QStringLiteral("kwinrc"))) 0085 , m_kxkbConfig() 0086 , m_operationMode(mode) 0087 { 0088 qRegisterMetaType<Options::WindowOperation>("Options::WindowOperation"); 0089 qRegisterMetaType<KWin::EffectWindow *>(); 0090 qRegisterMetaType<KWaylandServer::SurfaceInterface *>("KWaylandServer::SurfaceInterface *"); 0091 qRegisterMetaType<KSharedConfigPtr>(); 0092 qRegisterMetaType<std::chrono::nanoseconds>(); 0093 } 0094 0095 void Application::setConfigLock(bool lock) 0096 { 0097 m_configLock = lock; 0098 } 0099 0100 Application::OperationMode Application::operationMode() const 0101 { 0102 return m_operationMode; 0103 } 0104 0105 void Application::setOperationMode(OperationMode mode) 0106 { 0107 m_operationMode = mode; 0108 } 0109 0110 bool Application::shouldUseWaylandForCompositing() const 0111 { 0112 return m_operationMode == OperationModeWaylandOnly || m_operationMode == OperationModeXwayland; 0113 } 0114 0115 void Application::start() 0116 { 0117 // Prevent KWin from synchronously autostarting kactivitymanagerd 0118 // Indeed, kactivitymanagerd being a QApplication it will depend 0119 // on KWin startup... this is unsatisfactory dependency wise, 0120 // and it turns out that it leads to a deadlock in the Wayland case 0121 setProperty("org.kde.KActivities.core.disableAutostart", true); 0122 0123 setQuitOnLastWindowClosed(false); 0124 0125 if (!m_config->isImmutable() && m_configLock) { 0126 // TODO: This shouldn't be necessary 0127 // config->setReadOnly( true ); 0128 m_config->reparseConfiguration(); 0129 } 0130 if (!m_kxkbConfig) { 0131 m_kxkbConfig = KSharedConfig::openConfig(QStringLiteral("kxkbrc"), KConfig::NoGlobals); 0132 } 0133 0134 performStartup(); 0135 } 0136 0137 Application::~Application() 0138 { 0139 delete options; 0140 destroyAtoms(); 0141 destroyPlatform(); 0142 m_session.reset(); 0143 } 0144 0145 void Application::notifyStarted() 0146 { 0147 Q_EMIT started(); 0148 } 0149 0150 void Application::destroyAtoms() 0151 { 0152 delete atoms; 0153 atoms = nullptr; 0154 } 0155 0156 void Application::destroyPlatform() 0157 { 0158 m_outputBackend.reset(); 0159 } 0160 0161 void Application::resetCrashesCount() 0162 { 0163 crashes = 0; 0164 } 0165 0166 void Application::setCrashCount(int count) 0167 { 0168 crashes = count; 0169 } 0170 0171 bool Application::wasCrash() 0172 { 0173 return crashes > 0; 0174 } 0175 0176 void Application::createAboutData() 0177 { 0178 KAboutData aboutData(QStringLiteral("kwin"), // The program name used internally 0179 i18n("KWin"), // A displayable program name string 0180 QStringLiteral(KWIN_VERSION_STRING), // The program version string 0181 i18n("KDE window manager"), // Short description of what the app does 0182 KAboutLicense::GPL, // The license this code is released under 0183 i18n("(c) 1999-2019, The KDE Developers")); // Copyright Statement 0184 0185 aboutData.addAuthor(i18n("Matthias Ettrich"), QString(), QStringLiteral("ettrich@kde.org")); 0186 aboutData.addAuthor(i18n("Cristian Tibirna"), QString(), QStringLiteral("tibirna@kde.org")); 0187 aboutData.addAuthor(i18n("Daniel M. Duley"), QString(), QStringLiteral("mosfet@kde.org")); 0188 aboutData.addAuthor(i18n("Luboš Luňák"), QString(), QStringLiteral("l.lunak@kde.org")); 0189 aboutData.addAuthor(i18n("Martin Flöser"), QString(), QStringLiteral("mgraesslin@kde.org")); 0190 aboutData.addAuthor(i18n("David Edmundson"), QStringLiteral("Maintainer"), QStringLiteral("davidedmundson@kde.org")); 0191 aboutData.addAuthor(i18n("Roman Gilg"), QStringLiteral("Maintainer"), QStringLiteral("subdiff@gmail.com")); 0192 aboutData.addAuthor(i18n("Vlad Zahorodnii"), QStringLiteral("Maintainer"), QStringLiteral("vlad.zahorodnii@kde.org")); 0193 KAboutData::setApplicationData(aboutData); 0194 } 0195 0196 static const QString s_lockOption = QStringLiteral("lock"); 0197 static const QString s_crashesOption = QStringLiteral("crashes"); 0198 0199 void Application::setupCommandLine(QCommandLineParser *parser) 0200 { 0201 QCommandLineOption lockOption(s_lockOption, i18n("Disable configuration options")); 0202 QCommandLineOption crashesOption(s_crashesOption, i18n("Indicate that KWin has recently crashed n times"), QStringLiteral("n")); 0203 0204 parser->setApplicationDescription(i18n("KDE window manager")); 0205 parser->addOption(lockOption); 0206 parser->addOption(crashesOption); 0207 KAboutData::applicationData().setupCommandLine(parser); 0208 } 0209 0210 void Application::processCommandLine(QCommandLineParser *parser) 0211 { 0212 KAboutData aboutData = KAboutData::applicationData(); 0213 aboutData.processCommandLine(parser); 0214 setConfigLock(parser->isSet(s_lockOption)); 0215 Application::setCrashCount(parser->value(s_crashesOption).toInt()); 0216 } 0217 0218 void Application::setupTranslator() 0219 { 0220 QTranslator *qtTranslator = new QTranslator(qApp); 0221 qtTranslator->load("qt_" + QLocale::system().name(), 0222 QLibraryInfo::location(QLibraryInfo::TranslationsPath)); 0223 installTranslator(qtTranslator); 0224 } 0225 0226 void Application::setupMalloc() 0227 { 0228 #ifdef M_TRIM_THRESHOLD 0229 // Prevent fragmentation of the heap by malloc (glibc). 0230 // 0231 // The default threshold is 128*1024, which can result in a large memory usage 0232 // due to fragmentation especially if we use the raster graphicssystem. On the 0233 // otherside if the threshold is too low, free() starts to permanently ask the kernel 0234 // about shrinking the heap. 0235 const int pagesize = sysconf(_SC_PAGESIZE); 0236 mallopt(M_TRIM_THRESHOLD, 5 * pagesize); 0237 #endif // M_TRIM_THRESHOLD 0238 } 0239 0240 void Application::setupLocalizedString() 0241 { 0242 KLocalizedString::setApplicationDomain("kwin"); 0243 } 0244 0245 void Application::createWorkspace() 0246 { 0247 // we want all QQuickWindows with an alpha buffer, do here as Workspace might create QQuickWindows 0248 QQuickWindow::setDefaultAlphaBuffer(true); 0249 0250 // This tries to detect compositing options and can use GLX. GLX problems 0251 // (X errors) shouldn't cause kwin to abort, so this is out of the 0252 // critical startup section where x errors cause kwin to abort. 0253 0254 // create workspace. 0255 (void)new Workspace(); 0256 Q_EMIT workspaceCreated(); 0257 } 0258 0259 void Application::createInput() 0260 { 0261 #if KWIN_BUILD_SCREENLOCKER 0262 m_screenLockerWatcher = std::make_unique<ScreenLockerWatcher>(); 0263 #endif 0264 auto input = InputRedirection::create(this); 0265 input->init(); 0266 createPlatformCursor(this); 0267 } 0268 0269 void Application::createAtoms() 0270 { 0271 atoms = new Atoms; 0272 } 0273 0274 void Application::createOptions() 0275 { 0276 options = new Options; 0277 } 0278 0279 void Application::createPlugins() 0280 { 0281 m_pluginManager = std::make_unique<PluginManager>(); 0282 } 0283 0284 void Application::createColorManager() 0285 { 0286 m_colorManager = std::make_unique<ColorManager>(); 0287 } 0288 0289 void Application::createInputMethod() 0290 { 0291 m_inputMethod = std::make_unique<InputMethod>(); 0292 } 0293 0294 void Application::createTabletModeManager() 0295 { 0296 m_tabletModeManager = std::make_unique<TabletModeManager>(); 0297 } 0298 0299 void Application::installNativeX11EventFilter() 0300 { 0301 installNativeEventFilter(m_eventFilter.get()); 0302 } 0303 0304 void Application::removeNativeX11EventFilter() 0305 { 0306 removeNativeEventFilter(m_eventFilter.get()); 0307 } 0308 0309 void Application::destroyInput() 0310 { 0311 delete InputRedirection::self(); 0312 } 0313 0314 void Application::destroyWorkspace() 0315 { 0316 delete Workspace::self(); 0317 } 0318 0319 void Application::destroyCompositor() 0320 { 0321 delete Compositor::self(); 0322 } 0323 0324 void Application::destroyPlugins() 0325 { 0326 m_pluginManager.reset(); 0327 } 0328 0329 void Application::destroyColorManager() 0330 { 0331 m_colorManager.reset(); 0332 } 0333 0334 void Application::destroyInputMethod() 0335 { 0336 m_inputMethod.reset(); 0337 } 0338 0339 std::unique_ptr<Edge> Application::createScreenEdge(ScreenEdges *edges) 0340 { 0341 return std::make_unique<Edge>(edges); 0342 } 0343 0344 void Application::createPlatformCursor(QObject *parent) 0345 { 0346 new InputRedirectionCursor(parent); 0347 } 0348 0349 std::unique_ptr<OutlineVisual> Application::createOutline(Outline *outline) 0350 { 0351 if (Compositor::compositing()) { 0352 return std::make_unique<CompositedOutlineVisual>(outline); 0353 } 0354 return nullptr; 0355 } 0356 0357 void Application::createEffectsHandler(Compositor *compositor, WorkspaceScene *scene) 0358 { 0359 new EffectsHandlerImpl(compositor, scene); 0360 } 0361 0362 void Application::registerEventFilter(X11EventFilter *filter) 0363 { 0364 if (filter->isGenericEvent()) { 0365 m_genericEventFilters.append(new X11EventFilterContainer(filter)); 0366 } else { 0367 m_eventFilters.append(new X11EventFilterContainer(filter)); 0368 } 0369 } 0370 0371 static X11EventFilterContainer *takeEventFilter(X11EventFilter *eventFilter, 0372 QList<QPointer<X11EventFilterContainer>> &list) 0373 { 0374 for (int i = 0; i < list.count(); ++i) { 0375 X11EventFilterContainer *container = list.at(i); 0376 if (container->filter() == eventFilter) { 0377 return list.takeAt(i); 0378 } 0379 } 0380 return nullptr; 0381 } 0382 0383 void Application::setXwaylandScale(qreal scale) 0384 { 0385 if (scale != m_xwaylandScale) { 0386 m_xwaylandScale = scale; 0387 Q_EMIT xwaylandScaleChanged(); 0388 } 0389 } 0390 0391 void Application::unregisterEventFilter(X11EventFilter *filter) 0392 { 0393 X11EventFilterContainer *container = nullptr; 0394 if (filter->isGenericEvent()) { 0395 container = takeEventFilter(filter, m_genericEventFilters); 0396 } else { 0397 container = takeEventFilter(filter, m_eventFilters); 0398 } 0399 delete container; 0400 } 0401 0402 bool Application::dispatchEvent(xcb_generic_event_t *event) 0403 { 0404 static const QVector<QByteArray> s_xcbEerrors({QByteArrayLiteral("Success"), 0405 QByteArrayLiteral("BadRequest"), 0406 QByteArrayLiteral("BadValue"), 0407 QByteArrayLiteral("BadWindow"), 0408 QByteArrayLiteral("BadPixmap"), 0409 QByteArrayLiteral("BadAtom"), 0410 QByteArrayLiteral("BadCursor"), 0411 QByteArrayLiteral("BadFont"), 0412 QByteArrayLiteral("BadMatch"), 0413 QByteArrayLiteral("BadDrawable"), 0414 QByteArrayLiteral("BadAccess"), 0415 QByteArrayLiteral("BadAlloc"), 0416 QByteArrayLiteral("BadColor"), 0417 QByteArrayLiteral("BadGC"), 0418 QByteArrayLiteral("BadIDChoice"), 0419 QByteArrayLiteral("BadName"), 0420 QByteArrayLiteral("BadLength"), 0421 QByteArrayLiteral("BadImplementation"), 0422 QByteArrayLiteral("Unknown")}); 0423 0424 kwinApp()->updateX11Time(event); 0425 0426 const uint8_t x11EventType = event->response_type & ~0x80; 0427 if (!x11EventType) { 0428 // let's check whether it's an error from one of the extensions KWin uses 0429 xcb_generic_error_t *error = reinterpret_cast<xcb_generic_error_t *>(event); 0430 const QVector<Xcb::ExtensionData> extensions = Xcb::Extensions::self()->extensions(); 0431 for (const auto &extension : extensions) { 0432 if (error->major_code == extension.majorOpcode) { 0433 QByteArray errorName; 0434 if (error->error_code < s_xcbEerrors.size()) { 0435 errorName = s_xcbEerrors.at(error->error_code); 0436 } else if (error->error_code >= extension.errorBase) { 0437 const int index = error->error_code - extension.errorBase; 0438 if (index >= 0 && index < extension.errorCodes.size()) { 0439 errorName = extension.errorCodes.at(index); 0440 } 0441 } 0442 if (errorName.isEmpty()) { 0443 errorName = QByteArrayLiteral("Unknown"); 0444 } 0445 qCWarning(KWIN_CORE, "XCB error: %d (%s), sequence: %d, resource id: %d, major code: %d (%s), minor code: %d (%s)", 0446 int(error->error_code), errorName.constData(), 0447 int(error->sequence), int(error->resource_id), 0448 int(error->major_code), extension.name.constData(), 0449 int(error->minor_code), 0450 extension.opCodes.size() > error->minor_code ? extension.opCodes.at(error->minor_code).constData() : "Unknown"); 0451 return true; 0452 } 0453 } 0454 return false; 0455 } 0456 0457 if (x11EventType == XCB_GE_GENERIC) { 0458 xcb_ge_generic_event_t *ge = reinterpret_cast<xcb_ge_generic_event_t *>(event); 0459 0460 // We need to make a shadow copy of the event filter list because an activated event 0461 // filter may mutate it by removing or installing another event filter. 0462 const auto eventFilters = m_genericEventFilters; 0463 0464 for (X11EventFilterContainer *container : eventFilters) { 0465 if (!container) { 0466 continue; 0467 } 0468 X11EventFilter *filter = container->filter(); 0469 if (filter->extension() == ge->extension && filter->genericEventTypes().contains(ge->event_type) && filter->event(event)) { 0470 return true; 0471 } 0472 } 0473 } else { 0474 // We need to make a shadow copy of the event filter list because an activated event 0475 // filter may mutate it by removing or installing another event filter. 0476 const auto eventFilters = m_eventFilters; 0477 0478 for (X11EventFilterContainer *container : eventFilters) { 0479 if (!container) { 0480 continue; 0481 } 0482 X11EventFilter *filter = container->filter(); 0483 if (filter->eventTypes().contains(x11EventType) && filter->event(event)) { 0484 return true; 0485 } 0486 } 0487 } 0488 0489 if (workspace()) { 0490 return workspace()->workspaceEvent(event); 0491 } 0492 0493 return false; 0494 } 0495 0496 static quint32 monotonicTime() 0497 { 0498 timespec ts; 0499 0500 const int result = clock_gettime(CLOCK_MONOTONIC, &ts); 0501 if (result) { 0502 qCWarning(KWIN_CORE, "Failed to query monotonic time: %s", strerror(errno)); 0503 } 0504 0505 return ts.tv_sec * 1000 + ts.tv_nsec / 1000000L; 0506 } 0507 0508 void Application::updateXTime() 0509 { 0510 switch (operationMode()) { 0511 case Application::OperationModeX11: 0512 setX11Time(QX11Info::getTimestamp(), TimestampUpdate::Always); 0513 break; 0514 0515 case Application::OperationModeXwayland: 0516 setX11Time(monotonicTime(), TimestampUpdate::Always); 0517 break; 0518 0519 default: 0520 // Do not update the current X11 time stamp if it's the Wayland only session. 0521 break; 0522 } 0523 } 0524 0525 void Application::updateX11Time(xcb_generic_event_t *event) 0526 { 0527 xcb_timestamp_t time = XCB_TIME_CURRENT_TIME; 0528 const uint8_t eventType = event->response_type & ~0x80; 0529 switch (eventType) { 0530 case XCB_KEY_PRESS: 0531 case XCB_KEY_RELEASE: 0532 time = reinterpret_cast<xcb_key_press_event_t *>(event)->time; 0533 break; 0534 case XCB_BUTTON_PRESS: 0535 case XCB_BUTTON_RELEASE: 0536 time = reinterpret_cast<xcb_button_press_event_t *>(event)->time; 0537 break; 0538 case XCB_MOTION_NOTIFY: 0539 time = reinterpret_cast<xcb_motion_notify_event_t *>(event)->time; 0540 break; 0541 case XCB_ENTER_NOTIFY: 0542 case XCB_LEAVE_NOTIFY: 0543 time = reinterpret_cast<xcb_enter_notify_event_t *>(event)->time; 0544 break; 0545 case XCB_FOCUS_IN: 0546 case XCB_FOCUS_OUT: 0547 case XCB_KEYMAP_NOTIFY: 0548 case XCB_EXPOSE: 0549 case XCB_GRAPHICS_EXPOSURE: 0550 case XCB_NO_EXPOSURE: 0551 case XCB_VISIBILITY_NOTIFY: 0552 case XCB_CREATE_NOTIFY: 0553 case XCB_DESTROY_NOTIFY: 0554 case XCB_UNMAP_NOTIFY: 0555 case XCB_MAP_NOTIFY: 0556 case XCB_MAP_REQUEST: 0557 case XCB_REPARENT_NOTIFY: 0558 case XCB_CONFIGURE_NOTIFY: 0559 case XCB_CONFIGURE_REQUEST: 0560 case XCB_GRAVITY_NOTIFY: 0561 case XCB_RESIZE_REQUEST: 0562 case XCB_CIRCULATE_NOTIFY: 0563 case XCB_CIRCULATE_REQUEST: 0564 // no timestamp 0565 return; 0566 case XCB_PROPERTY_NOTIFY: 0567 time = reinterpret_cast<xcb_property_notify_event_t *>(event)->time; 0568 break; 0569 case XCB_SELECTION_CLEAR: 0570 time = reinterpret_cast<xcb_selection_clear_event_t *>(event)->time; 0571 break; 0572 case XCB_SELECTION_REQUEST: 0573 time = reinterpret_cast<xcb_selection_request_event_t *>(event)->time; 0574 break; 0575 case XCB_SELECTION_NOTIFY: 0576 time = reinterpret_cast<xcb_selection_notify_event_t *>(event)->time; 0577 break; 0578 case XCB_COLORMAP_NOTIFY: 0579 case XCB_CLIENT_MESSAGE: 0580 case XCB_MAPPING_NOTIFY: 0581 case XCB_GE_GENERIC: 0582 // no timestamp 0583 return; 0584 default: 0585 // extension handling 0586 if (Xcb::Extensions::self()) { 0587 if (eventType == Xcb::Extensions::self()->shapeNotifyEvent()) { 0588 time = reinterpret_cast<xcb_shape_notify_event_t *>(event)->server_time; 0589 } 0590 if (eventType == Xcb::Extensions::self()->damageNotifyEvent()) { 0591 time = reinterpret_cast<xcb_damage_notify_event_t *>(event)->timestamp; 0592 } 0593 } 0594 break; 0595 } 0596 setX11Time(time); 0597 } 0598 0599 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0600 bool XcbEventFilter::nativeEventFilter(const QByteArray &eventType, void *message, long int *result) 0601 #else 0602 bool XcbEventFilter::nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) 0603 #endif 0604 { 0605 if (eventType == "xcb_generic_event_t") { 0606 return kwinApp()->dispatchEvent(static_cast<xcb_generic_event_t *>(message)); 0607 } 0608 return false; 0609 } 0610 0611 QProcessEnvironment Application::processStartupEnvironment() const 0612 { 0613 return m_processEnvironment; 0614 } 0615 0616 void Application::setProcessStartupEnvironment(const QProcessEnvironment &environment) 0617 { 0618 m_processEnvironment = environment; 0619 } 0620 0621 void Application::setOutputBackend(std::unique_ptr<OutputBackend> &&backend) 0622 { 0623 Q_ASSERT(!m_outputBackend); 0624 m_outputBackend = std::move(backend); 0625 } 0626 0627 void Application::setSession(std::unique_ptr<Session> &&session) 0628 { 0629 Q_ASSERT(!m_session); 0630 m_session = std::move(session); 0631 } 0632 0633 PluginManager *Application::pluginManager() const 0634 { 0635 return m_pluginManager.get(); 0636 } 0637 0638 InputMethod *Application::inputMethod() const 0639 { 0640 return m_inputMethod.get(); 0641 } 0642 0643 ColorManager *Application::colorManager() const 0644 { 0645 return m_colorManager.get(); 0646 } 0647 0648 XwaylandInterface *Application::xwayland() const 0649 { 0650 return nullptr; 0651 } 0652 0653 #if KWIN_BUILD_SCREENLOCKER 0654 ScreenLockerWatcher *Application::screenLockerWatcher() const 0655 { 0656 return m_screenLockerWatcher.get(); 0657 } 0658 #endif 0659 0660 PlatformCursorImage Application::cursorImage() const 0661 { 0662 Cursor *cursor = Cursors::self()->currentCursor(); 0663 return PlatformCursorImage(cursor->image(), cursor->hotspot()); 0664 } 0665 0666 void Application::startInteractiveWindowSelection(std::function<void(KWin::Window *)> callback, const QByteArray &cursorName) 0667 { 0668 if (!input()) { 0669 callback(nullptr); 0670 return; 0671 } 0672 input()->startInteractiveWindowSelection(callback, cursorName); 0673 } 0674 0675 void Application::startInteractivePositionSelection(std::function<void(const QPoint &)> callback) 0676 { 0677 if (!input()) { 0678 callback(QPoint(-1, -1)); 0679 return; 0680 } 0681 input()->startInteractivePositionSelection(callback); 0682 } 0683 0684 } // namespace