diff --git a/kwinbindings.cpp b/kwinbindings.cpp index 4448f7680..4ea642932 100644 --- a/kwinbindings.cpp +++ b/kwinbindings.cpp @@ -1,163 +1,166 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 1999, 2000 Matthias Ettrich Copyright (C) 2003 Lubos Lunak This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ // This file is #included from within: // Workspace::initShortcuts() // { // Some shortcuts have Tarzan-speech like names, they need extra // normal human descriptions with DEF2() the others can use DEF() // new DEF3 allows to pass data to the action, replacing the %1 argument in the name #define DEF2( name, descr, key, fnSlot ) \ initShortcut(QStringLiteral(name), i18n(descr), key, &Workspace::fnSlot); #define DEF( name, key, fnSlot ) \ initShortcut(QStringLiteral(name), i18n(name), key, &Workspace::fnSlot); #define DEF3( name, key, fnSlot, value ) \ initShortcut(QStringLiteral(name).arg(value), i18n(name, value), key, &Workspace::fnSlot, value); #define DEF4( name, descr, key, functor ) \ initShortcut(QStringLiteral(name), i18n(descr), key, functor); +#define DEF5( name, key, functor, value ) \ + initShortcut(QStringLiteral(name).arg(value), i18n(name, value), key, functor, value); + DEF(I18N_NOOP("Walk Through Window Tabs"), 0, slotActivateNextTab); DEF(I18N_NOOP("Walk Through Window Tabs (Reverse)"), 0, slotActivatePrevTab); DEF(I18N_NOOP("Remove Window From Group"), 0, slotUntab); DEF(I18N_NOOP("Window Operations Menu"), Qt::ALT + Qt::Key_F3, slotWindowOperations); DEF2("Window Close", I18N_NOOP("Close Window"), Qt::ALT + Qt::Key_F4, slotWindowClose); DEF2("Window Maximize", I18N_NOOP("Maximize Window"), 0, slotWindowMaximize); DEF2("Window Maximize Vertical", I18N_NOOP("Maximize Window Vertically"), 0, slotWindowMaximizeVertical); DEF2("Window Maximize Horizontal", I18N_NOOP("Maximize Window Horizontally"), 0, slotWindowMaximizeHorizontal); DEF2("Window Minimize", I18N_NOOP("Minimize Window"), 0, slotWindowMinimize); DEF2("Window Shade", I18N_NOOP("Shade Window"), 0, slotWindowShade); DEF2("Window Move", I18N_NOOP("Move Window"), 0, slotWindowMove); DEF2("Window Resize", I18N_NOOP("Resize Window"), 0, slotWindowResize); DEF2("Window Raise", I18N_NOOP("Raise Window"), 0, slotWindowRaise); DEF2("Window Lower", I18N_NOOP("Lower Window"), 0, slotWindowLower); DEF(I18N_NOOP("Toggle Window Raise/Lower"), 0, slotWindowRaiseOrLower); DEF2("Window Fullscreen", I18N_NOOP("Make Window Fullscreen"), 0, slotWindowFullScreen); DEF2("Window No Border", I18N_NOOP("Hide Window Border"), 0, slotWindowNoBorder); DEF2("Window Above Other Windows", I18N_NOOP("Keep Window Above Others"), 0, slotWindowAbove); DEF2("Window Below Other Windows", I18N_NOOP("Keep Window Below Others"), 0, slotWindowBelow); DEF(I18N_NOOP("Activate Window Demanding Attention"), Qt::CTRL + Qt::ALT + Qt::Key_A, slotActivateAttentionWindow); DEF(I18N_NOOP("Setup Window Shortcut"), 0, slotSetupWindowShortcut); DEF2("Window Pack Right", I18N_NOOP("Pack Window to the Right"), 0, slotWindowPackRight); DEF2("Window Pack Left", I18N_NOOP("Pack Window to the Left"), 0, slotWindowPackLeft); DEF2("Window Pack Up", I18N_NOOP("Pack Window Up"), 0, slotWindowPackUp); DEF2("Window Pack Down", I18N_NOOP("Pack Window Down"), 0, slotWindowPackDown); DEF2("Window Grow Horizontal", I18N_NOOP("Pack Grow Window Horizontally"), 0, slotWindowGrowHorizontal); DEF2("Window Grow Vertical", I18N_NOOP("Pack Grow Window Vertically"), 0, slotWindowGrowVertical); DEF2("Window Shrink Horizontal", I18N_NOOP("Pack Shrink Window Horizontally"), 0, slotWindowShrinkHorizontal); DEF2("Window Shrink Vertical", I18N_NOOP("Pack Shrink Window Vertically"), 0, slotWindowShrinkVertical); DEF4("Window Quick Tile Left", I18N_NOOP("Quick Tile Window to the Left"), 0, std::bind(&Workspace::quickTileWindow, this, QuickTileFlag::Left)); DEF4("Window Quick Tile Right", I18N_NOOP("Quick Tile Window to the Right"), 0, std::bind(&Workspace::quickTileWindow, this, QuickTileFlag::Right)); DEF4("Window Quick Tile Top", I18N_NOOP("Quick Tile Window to the Top"), 0, std::bind(&Workspace::quickTileWindow, this, QuickTileFlag::Top)); DEF4("Window Quick Tile Bottom", I18N_NOOP("Quick Tile Window to the Bottom"), 0, std::bind(&Workspace::quickTileWindow, this, QuickTileFlag::Bottom)); DEF4("Window Quick Tile Top Left", I18N_NOOP("Quick Tile Window to the Top Left"), 0, std::bind(&Workspace::quickTileWindow, this, QuickTileFlag::Top | QuickTileFlag::Left)); DEF4("Window Quick Tile Bottom Left", I18N_NOOP("Quick Tile Window to the Bottom Left"), 0, std::bind(&Workspace::quickTileWindow, this, QuickTileFlag::Bottom | QuickTileFlag::Left)); DEF4("Window Quick Tile Top Right", I18N_NOOP("Quick Tile Window to the Top Right"), 0, std::bind(&Workspace::quickTileWindow, this, QuickTileFlag::Top | QuickTileFlag::Right)); DEF4("Window Quick Tile Bottom Right", I18N_NOOP("Quick Tile Window to the Bottom Right"), 0, std::bind(&Workspace::quickTileWindow, this, QuickTileFlag::Bottom | QuickTileFlag::Right)); DEF4("Switch Window Up", I18N_NOOP("Switch to Window Above"), Qt::META + Qt::ALT + Qt::Key_Up, std::bind(static_cast(&Workspace::switchWindow), this, DirectionNorth)); DEF4("Switch Window Down", I18N_NOOP("Switch to Window Below"), Qt::META + Qt::ALT + Qt::Key_Down, std::bind(static_cast(&Workspace::switchWindow), this, DirectionSouth)); DEF4("Switch Window Right", I18N_NOOP("Switch to Window to the Right"), Qt::META + Qt::ALT + Qt::Key_Right, std::bind(static_cast(&Workspace::switchWindow), this, DirectionEast)); DEF4("Switch Window Left", I18N_NOOP("Switch to Window to the Left"), Qt::META + Qt::ALT + Qt::Key_Left, std::bind(static_cast(&Workspace::switchWindow), this, DirectionWest)); DEF2("Increase Opacity", I18N_NOOP("Increase Opacity of Active Window by 5 %"), 0, slotIncreaseWindowOpacity); DEF2("Decrease Opacity", I18N_NOOP("Decrease Opacity of Active Window by 5 %"), 0, slotLowerWindowOpacity); DEF2("Window On All Desktops", I18N_NOOP("Keep Window on All Desktops"), 0, slotWindowOnAllDesktops); for (int i = 1; i < 21; ++i) { - DEF3(I18N_NOOP("Window to Desktop %1"), 0, slotWindowToDesktop, i); + DEF5(I18N_NOOP("Window to Desktop %1"), 0, std::bind(&Workspace::slotWindowToDesktop, this, i), i); } DEF(I18N_NOOP("Window to Next Desktop"), 0, slotWindowToNextDesktop); DEF(I18N_NOOP("Window to Previous Desktop"), 0, slotWindowToPreviousDesktop); DEF(I18N_NOOP("Window One Desktop to the Right"), 0, slotWindowToDesktopRight); DEF(I18N_NOOP("Window One Desktop to the Left"), 0, slotWindowToDesktopLeft); DEF(I18N_NOOP("Window One Desktop Up"), 0, slotWindowToDesktopUp); DEF(I18N_NOOP("Window One Desktop Down"), 0, slotWindowToDesktopDown); for (int i = 0; i < 8; ++i) { DEF3(I18N_NOOP("Window to Screen %1"), 0, slotWindowToScreen, i); } DEF(I18N_NOOP("Window to Next Screen"), 0, slotWindowToNextScreen); DEF(I18N_NOOP("Window to Previous Screen"), 0, slotWindowToPrevScreen); DEF(I18N_NOOP("Show Desktop"), 0, slotToggleShowDesktop); for (int i = 0; i < 8; ++i) { DEF3(I18N_NOOP("Switch to Screen %1"), 0, slotSwitchToScreen, i); } DEF(I18N_NOOP("Switch to Next Screen"), 0, slotSwitchToNextScreen); DEF(I18N_NOOP("Switch to Previous Screen"), 0, slotSwitchToPrevScreen); DEF(I18N_NOOP("Kill Window"), Qt::CTRL + Qt::ALT + Qt::Key_Escape, slotKillWindow); DEF(I18N_NOOP("Suspend Compositing"), Qt::SHIFT + Qt::ALT + Qt::Key_F12, slotToggleCompositing); DEF(I18N_NOOP("Invert Screen Colors"), 0, slotInvertScreen); #undef DEF #undef DEF2 #undef DEF3 // } diff --git a/useractions.cpp b/useractions.cpp index 850d82623..c3a0360dc 100644 --- a/useractions.cpp +++ b/useractions.cpp @@ -1,1872 +1,1871 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 1999, 2000 Matthias Ettrich Copyright (C) 2003 Lubos Lunak This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ /* This file contains things relevant to direct user actions, such as responses to global keyboard shortcuts, or selecting actions from the window operations menu. */ /////////////////////////////////////////////////////////////////////////////// // NOTE: if you change the menu, keep kde-workspace/libs/taskmanager/taskactions.cpp in sync ////////////////////////////////////////////////////////////////////////////// #include "useractions.h" #include "cursor.h" #include "client.h" #include "input.h" #include "workspace.h" #include "effects.h" #include "platform.h" #include "screens.h" #include "virtualdesktops.h" #include "scripting/scripting.h" #ifdef KWIN_BUILD_ACTIVITIES #include "activities.h" #include #endif #include "appmenu.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "killwindow.h" #ifdef KWIN_BUILD_TABBOX #include "tabbox.h" #endif namespace KWin { UserActionsMenu::UserActionsMenu(QObject *parent) : QObject(parent) , m_menu(NULL) , m_desktopMenu(NULL) , m_screenMenu(NULL) , m_activityMenu(NULL) , m_addTabsMenu(NULL) , m_switchToTabMenu(NULL) , m_scriptsMenu(NULL) , m_resizeOperation(NULL) , m_moveOperation(NULL) , m_maximizeOperation(NULL) , m_shadeOperation(NULL) , m_keepAboveOperation(NULL) , m_keepBelowOperation(NULL) , m_fullScreenOperation(NULL) , m_noBorderOperation(NULL) , m_minimizeOperation(NULL) , m_closeOperation(NULL) , m_removeFromTabGroup(NULL) , m_closeTabGroup(NULL) , m_client(QWeakPointer()) { } UserActionsMenu::~UserActionsMenu() { discard(); } bool UserActionsMenu::isShown() const { return m_menu && m_menu->isVisible(); } bool UserActionsMenu::hasClient() const { return !m_client.isNull() && isShown(); } void UserActionsMenu::close() { if (!m_menu) { return; } m_menu->close(); m_client.clear(); } bool UserActionsMenu::isMenuClient(const AbstractClient *c) const { if (!c || m_client.isNull()) { return false; } return c == m_client.data(); } void UserActionsMenu::show(const QRect &pos, const QWeakPointer &cl) { if (!KAuthorized::authorizeAction(QStringLiteral("kwin_rmb"))) return; if (cl.isNull()) return; if (isShown()) // recursion return; if (cl.data()->isDesktop() || cl.data()->isDock()) return; m_client = cl; init(); Workspace *ws = Workspace::self(); int x = pos.left(); int y = pos.bottom(); const bool needsPopup = kwinApp()->shouldUseWaylandForCompositing(); if (y == pos.top()) { m_client.data()->blockActivityUpdates(true); if (needsPopup) { m_menu->popup(QPoint(x, y)); } else { m_menu->exec(QPoint(x, y)); } if (!m_client.isNull()) m_client.data()->blockActivityUpdates(false); } else { m_client.data()->blockActivityUpdates(true); QRect area = ws->clientArea(ScreenArea, QPoint(x, y), VirtualDesktopManager::self()->current()); menuAboutToShow(); // needed for sizeHint() to be correct :-/ int popupHeight = m_menu->sizeHint().height(); if (y + popupHeight < area.height()) { if (needsPopup) { m_menu->popup(QPoint(x, y)); } else { m_menu->exec(QPoint(x, y)); } } else { if (needsPopup) { m_menu->popup(QPoint(x, pos.top() - popupHeight)); } else { m_menu->exec(QPoint(x, pos.top() - popupHeight)); } } if (!m_client.isNull()) m_client.data()->blockActivityUpdates(true); } } void UserActionsMenu::grabInput() { m_menu->windowHandle()->setMouseGrabEnabled(true); m_menu->windowHandle()->setKeyboardGrabEnabled(true); } void UserActionsMenu::helperDialog(const QString& message, const QWeakPointer &c) { QStringList args; QString type; auto shortcut = [](const QString &name) { QAction* action = Workspace::self()->findChild(name); assert(action != NULL); const auto shortcuts = KGlobalAccel::self()->shortcut(action); return QStringLiteral("%1 (%2)").arg(action->text()) .arg(shortcuts.isEmpty() ? QString() : shortcuts.first().toString(QKeySequence::NativeText)); }; if (message == QStringLiteral("noborderaltf3")) { args << QStringLiteral("--msgbox") << i18n( "You have selected to show a window without its border.\n" "Without the border, you will not be able to enable the border " "again using the mouse: use the window operations menu instead, " "activated using the %1 keyboard shortcut.", shortcut(QStringLiteral("Window Operations Menu"))); type = QStringLiteral("altf3warning"); } else if (message == QLatin1String("fullscreenaltf3")) { args << QStringLiteral("--msgbox") << i18n( "You have selected to show a window in fullscreen mode.\n" "If the application itself does not have an option to turn the fullscreen " "mode off you will not be able to disable it " "again using the mouse: use the window operations menu instead, " "activated using the %1 keyboard shortcut.", shortcut(QStringLiteral("Window Operations Menu"))); type = QStringLiteral("altf3warning"); } else abort(); if (!type.isEmpty()) { KConfig cfg(QStringLiteral("kwin_dialogsrc")); KConfigGroup cg(&cfg, "Notification Messages"); // Depends on KMessageBox if (!cg.readEntry(type, true)) return; args << QStringLiteral("--dontagain") << QLatin1String("kwin_dialogsrc:") + type; } if (!c.isNull()) args << QStringLiteral("--embed") << QString::number(c.data()->windowId()); QtConcurrent::run([args]() { KProcess::startDetached(QStringLiteral("kdialog"), args); }); } QStringList configModules(bool controlCenter) { QStringList args; args << QStringLiteral("kwindecoration"); if (controlCenter) args << QStringLiteral("kwinoptions"); else if (KAuthorized::authorizeControlModule(QStringLiteral("kde-kwinoptions.desktop"))) args << QStringLiteral("kwinactions") << QStringLiteral("kwinfocus") << QStringLiteral("kwinmoving") << QStringLiteral("kwinadvanced") << QStringLiteral("kwinrules") << QStringLiteral("kwincompositing") << QStringLiteral("kwineffects") #ifdef KWIN_BUILD_TABBOX << QStringLiteral("kwintabbox") #endif << QStringLiteral("kwinscreenedges") << QStringLiteral("kwinscripts") ; return args; } void UserActionsMenu::init() { if (m_menu) { return; } m_menu = new QMenu; connect(m_menu, &QMenu::aboutToShow, this, &UserActionsMenu::menuAboutToShow); connect(m_menu, &QMenu::triggered, this, &UserActionsMenu::slotWindowOperation, Qt::QueuedConnection); QMenu *advancedMenu = new QMenu(m_menu); connect(advancedMenu, &QMenu::aboutToShow, [this, advancedMenu]() { advancedMenu->setPalette(m_client.data()->palette()); }); auto setShortcut = [](QAction *action, const QString &actionName) { const auto shortcuts = KGlobalAccel::self()->shortcut(Workspace::self()->findChild(actionName)); if (!shortcuts.isEmpty()) { action->setShortcut(shortcuts.first()); } }; m_moveOperation = advancedMenu->addAction(i18n("&Move")); m_moveOperation->setIcon(QIcon::fromTheme(QStringLiteral("transform-move"))); setShortcut(m_moveOperation, QStringLiteral("Window Move")); m_moveOperation->setData(Options::UnrestrictedMoveOp); m_resizeOperation = advancedMenu->addAction(i18n("&Resize")); setShortcut(m_resizeOperation, QStringLiteral("Window Resize")); m_resizeOperation->setData(Options::ResizeOp); m_keepAboveOperation = advancedMenu->addAction(i18n("Keep &Above Others")); m_keepAboveOperation->setIcon(QIcon::fromTheme(QStringLiteral("go-up"))); setShortcut(m_keepAboveOperation, QStringLiteral("Window Above Other Windows")); m_keepAboveOperation->setCheckable(true); m_keepAboveOperation->setData(Options::KeepAboveOp); m_keepBelowOperation = advancedMenu->addAction(i18n("Keep &Below Others")); m_keepBelowOperation->setIcon(QIcon::fromTheme(QStringLiteral("go-down"))); setShortcut(m_keepBelowOperation, QStringLiteral("Window Below Other Windows")); m_keepBelowOperation->setCheckable(true); m_keepBelowOperation->setData(Options::KeepBelowOp); m_fullScreenOperation = advancedMenu->addAction(i18n("&Fullscreen")); m_fullScreenOperation->setIcon(QIcon::fromTheme(QStringLiteral("view-fullscreen"))); setShortcut(m_fullScreenOperation, QStringLiteral("Window Fullscreen")); m_fullScreenOperation->setCheckable(true); m_fullScreenOperation->setData(Options::FullScreenOp); m_shadeOperation = advancedMenu->addAction(i18n("&Shade")); setShortcut(m_shadeOperation, QStringLiteral("Window Shade")); m_shadeOperation->setCheckable(true); m_shadeOperation->setData(Options::ShadeOp); m_noBorderOperation = advancedMenu->addAction(i18n("&No Border")); setShortcut(m_noBorderOperation, QStringLiteral("Window No Border")); m_noBorderOperation->setCheckable(true); m_noBorderOperation->setData(Options::NoBorderOp); advancedMenu->addSeparator(); m_shortcutOperation = advancedMenu->addAction(i18n("Window Short&cut...")); m_shortcutOperation->setIcon(QIcon::fromTheme(QStringLiteral("configure-shortcuts"))); setShortcut(m_shortcutOperation, QStringLiteral("Setup Window Shortcut")); m_shortcutOperation->setData(Options::SetupWindowShortcutOp); QAction *action = advancedMenu->addAction(i18n("Special &Window Settings...")); action->setIcon(QIcon::fromTheme(QStringLiteral("preferences-system-windows-actions"))); action->setData(Options::WindowRulesOp); action = advancedMenu->addAction(i18n("S&pecial Application Settings...")); action->setIcon(QIcon::fromTheme(QStringLiteral("preferences-system-windows-actions"))); action->setData(Options::ApplicationRulesOp); if (!kwinApp()->config()->isImmutable() && !KAuthorized::authorizeControlModules(configModules(true)).isEmpty()) { advancedMenu->addSeparator(); action = advancedMenu->addAction(i18nc("Entry in context menu of window decoration to open the configuration module of KWin", "Window Manager S&ettings...")); action->setIcon(QIcon::fromTheme(QStringLiteral("configure"))); connect(action, &QAction::triggered, this, [this]() { // opens the KWin configuration QStringList args; args << QStringLiteral("--icon") << QStringLiteral("preferences-system-windows"); const QString path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kservices5/kwinfocus.desktop")); if (!path.isEmpty()) { args << QStringLiteral("--desktopfile") << path; } args << configModules(false); QProcess *p = new Process(this); p->setArguments(args); p->setProcessEnvironment(kwinApp()->processStartupEnvironment()); p->setProgram(QStringLiteral("kcmshell5")); connect(p, static_cast(&QProcess::finished), p, &QProcess::deleteLater); connect(p, static_cast(&QProcess::error), this, [p] (QProcess::ProcessError e) { if (e == QProcess::FailedToStart) { qCDebug(KWIN_CORE) << "Failed to start kcmshell5"; } } ); p->start(); } ); } m_minimizeOperation = m_menu->addAction(i18n("Mi&nimize")); setShortcut(m_minimizeOperation, QStringLiteral("Window Minimize")); m_minimizeOperation->setData(Options::MinimizeOp); m_maximizeOperation = m_menu->addAction(i18n("Ma&ximize")); setShortcut(m_maximizeOperation, QStringLiteral("Window Maximize")); m_maximizeOperation->setCheckable(true); m_maximizeOperation->setData(Options::MaximizeOp); m_menu->addSeparator(); // Actions for window tabbing if (false) { m_removeFromTabGroup = m_menu->addAction(i18n("&Untab")); setShortcut(m_removeFromTabGroup, QStringLiteral("Untab")); m_removeFromTabGroup->setData(Options::RemoveTabFromGroupOp); m_closeTabGroup = m_menu->addAction(i18n("Close Entire &Group")); m_closeTabGroup->setIcon(QIcon::fromTheme(QStringLiteral("window-close"))); setShortcut(m_closeTabGroup, QStringLiteral("Close TabGroup")); m_closeTabGroup->setData(Options::CloseTabGroupOp); m_menu->addSeparator(); } m_menu->addSeparator(); action = m_menu->addMenu(advancedMenu); action->setText(i18n("&More Actions")); m_menu->addSeparator(); m_closeOperation = m_menu->addAction(i18n("&Close")); m_closeOperation->setIcon(QIcon::fromTheme(QStringLiteral("window-close"))); setShortcut(m_closeOperation, QStringLiteral("Window Close")); m_closeOperation->setData(Options::CloseOp); } void UserActionsMenu::discard() { delete m_menu; m_menu = NULL; m_desktopMenu = NULL; m_screenMenu = NULL; m_activityMenu = NULL; m_switchToTabMenu = NULL; m_addTabsMenu = NULL; m_scriptsMenu = NULL; } void UserActionsMenu::menuAboutToShow() { if (m_client.isNull() || !m_menu) return; if (VirtualDesktopManager::self()->count() == 1) { delete m_desktopMenu; m_desktopMenu = 0; } else { initDesktopPopup(); } if (screens()->count() == 1 || (!m_client.data()->isMovable() && !m_client.data()->isMovableAcrossScreens())) { delete m_screenMenu; m_screenMenu = NULL; } else { initScreenPopup(); } m_menu->setPalette(m_client.data()->palette()); m_resizeOperation->setEnabled(m_client.data()->isResizable()); m_moveOperation->setEnabled(m_client.data()->isMovableAcrossScreens()); m_maximizeOperation->setEnabled(m_client.data()->isMaximizable()); m_maximizeOperation->setChecked(m_client.data()->maximizeMode() == MaximizeFull); m_shadeOperation->setEnabled(m_client.data()->isShadeable()); m_shadeOperation->setChecked(m_client.data()->shadeMode() != ShadeNone); m_keepAboveOperation->setChecked(m_client.data()->keepAbove()); m_keepBelowOperation->setChecked(m_client.data()->keepBelow()); m_fullScreenOperation->setEnabled(m_client.data()->userCanSetFullScreen()); m_fullScreenOperation->setChecked(m_client.data()->isFullScreen()); m_noBorderOperation->setEnabled(m_client.data()->userCanSetNoBorder()); m_noBorderOperation->setChecked(m_client.data()->noBorder()); m_minimizeOperation->setEnabled(m_client.data()->isMinimizable()); m_closeOperation->setEnabled(m_client.data()->isCloseable()); m_shortcutOperation->setEnabled(m_client.data()->rules()->checkShortcut(QString()).isNull()); if (false) { initTabbingPopups(); m_addTabsMenu->setPalette(m_client.data()->palette()); } else { delete m_addTabsMenu; m_addTabsMenu = 0; } // drop the existing scripts menu delete m_scriptsMenu; m_scriptsMenu = NULL; // ask scripts whether they want to add entries for the given Client QList scriptActions = Scripting::self()->actionsForUserActionMenu(m_client.data(), m_scriptsMenu); if (!scriptActions.isEmpty()) { m_scriptsMenu = new QMenu(m_menu); m_scriptsMenu->setPalette(m_client.data()->palette()); m_scriptsMenu->addActions(scriptActions); QAction *action = m_scriptsMenu->menuAction(); // set it as the first item after desktop m_menu->insertAction(m_closeOperation, action); action->setText(i18n("&Extensions")); } showHideActivityMenu(); } void UserActionsMenu::showHideActivityMenu() { #ifdef KWIN_BUILD_ACTIVITIES if (!Activities::self()) { return; } const QStringList &openActivities_ = Activities::self()->running(); qCDebug(KWIN_CORE) << "activities:" << openActivities_.size(); if (openActivities_.size() < 2) { delete m_activityMenu; m_activityMenu = 0; } else { initActivityPopup(); } #endif } void UserActionsMenu::selectPopupClientTab(QAction* action) { if (!(!m_client.isNull() && m_client.data()->tabGroup()) || !action->data().isValid()) return; if (Client *other = action->data().value()) { m_client.data()->tabGroup()->setCurrent(other); return; } // failed conversion, try "1" & "2", being prev and next int direction = action->data().toInt(); if (direction == 1) m_client.data()->tabGroup()->activatePrev(); else if (direction == 2) m_client.data()->tabGroup()->activateNext(); } static QString shortCaption(const QString &s) { if (s.length() < 64) return s; QString ss = s; return ss.replace(32,s.length()-64, QStringLiteral("...")); } void UserActionsMenu::rebuildTabListPopup() { Q_ASSERT(m_switchToTabMenu); m_switchToTabMenu->clear(); // whatever happens "0x1" and "0x2" are no heap positions ;-) m_switchToTabMenu->addAction(i18nc("Switch to tab -> Previous", "Previous"))->setData(1); m_switchToTabMenu->addAction(i18nc("Switch to tab -> Next", "Next"))->setData(2); m_switchToTabMenu->addSeparator(); for (QList::const_iterator i = m_client.data()->tabGroup()->clients().constBegin(), end = m_client.data()->tabGroup()->clients().constEnd(); i != end; ++i) { if ((*i)->noBorder() || *i == m_client.data()->tabGroup()->current()) continue; // cannot tab there anyway m_switchToTabMenu->addAction(shortCaption((*i)->caption()))->setData(QVariant::fromValue(*i)); } } void UserActionsMenu::entabPopupClient(QAction* action) { if (m_client.isNull() || !action->data().isValid()) return; Client *other = dynamic_cast(action->data().value()); if (!Workspace::self()->clientList().contains(other)) // might have been lost betwenn pop-up and selection return; Client *c = dynamic_cast(m_client.data()); if (!c) { return; } c->tabBehind(other, true); if (options->focusPolicyIsReasonable()) Workspace::self()->requestFocus(c); } void UserActionsMenu::rebuildTabGroupPopup() { Q_ASSERT(m_addTabsMenu); m_addTabsMenu->clear(); QList handled; const ClientList &clientList = Workspace::self()->clientList(); for (QList::const_iterator i = clientList.constBegin(), end = clientList.constEnd(); i != end; ++i) { if (*i == m_client.data() || (*i)->noBorder()) continue; m_addTabsMenu->addAction(shortCaption((*i)->caption()))->setData(QVariant::fromValue(*i)); } if (m_addTabsMenu->actions().isEmpty()) m_addTabsMenu->addAction(i18nc("There's no window available to be attached as tab to this one", "None available"))->setEnabled(false); } void UserActionsMenu::initTabbingPopups() { bool needTabManagers = false; if (m_client.data()->tabGroup() && m_client.data()->tabGroup()->count() > 1) { needTabManagers = true; if (!m_switchToTabMenu) { m_switchToTabMenu = new QMenu(i18n("Switch to Tab"), m_menu); connect(m_switchToTabMenu, &QMenu::triggered, this, &UserActionsMenu::selectPopupClientTab); connect(m_switchToTabMenu, &QMenu::aboutToShow, this, &UserActionsMenu::rebuildTabListPopup); m_menu->insertMenu(m_removeFromTabGroup, m_switchToTabMenu); } } else { delete m_switchToTabMenu; m_switchToTabMenu = 0; } if (!m_addTabsMenu) { m_addTabsMenu = new QMenu(i18n("&Attach as tab to"), m_menu); connect(m_addTabsMenu, &QMenu::triggered, this, &UserActionsMenu::entabPopupClient); connect(m_addTabsMenu, &QMenu::aboutToShow, this, &UserActionsMenu::rebuildTabGroupPopup); m_menu->insertMenu(m_removeFromTabGroup, m_addTabsMenu); } m_addTabsMenu->menuAction()->setEnabled(!m_client.data()->isFullScreen()); m_removeFromTabGroup->setVisible(needTabManagers); m_closeTabGroup->setVisible(needTabManagers); } void UserActionsMenu::initDesktopPopup() { if (m_desktopMenu) return; m_desktopMenu = new QMenu(m_menu); connect(m_desktopMenu, &QMenu::triggered, this, &UserActionsMenu::slotSendToDesktop); connect(m_desktopMenu, &QMenu::aboutToShow, this, &UserActionsMenu::desktopPopupAboutToShow); QAction *action = m_desktopMenu->menuAction(); // set it as the first item m_menu->insertAction(m_minimizeOperation, action); action->setText(i18n("Move To &Desktop")); } void UserActionsMenu::initScreenPopup() { if (m_screenMenu) { return; } m_screenMenu = new QMenu(m_menu); connect(m_screenMenu, &QMenu::triggered, this, &UserActionsMenu::slotSendToScreen); connect(m_screenMenu, &QMenu::aboutToShow, this, &UserActionsMenu::screenPopupAboutToShow); QAction *action = m_screenMenu->menuAction(); // set it as the first item after desktop m_menu->insertAction(m_activityMenu ? m_activityMenu->menuAction() : m_minimizeOperation, action); action->setText(i18n("Move To &Screen")); } void UserActionsMenu::initActivityPopup() { if (m_activityMenu) return; m_activityMenu = new QMenu(m_menu); connect(m_activityMenu, &QMenu::triggered, this, &UserActionsMenu::slotToggleOnActivity); connect(m_activityMenu, &QMenu::aboutToShow, this, &UserActionsMenu::activityPopupAboutToShow); QAction *action = m_activityMenu->menuAction(); // set it as the first item m_menu->insertAction(m_minimizeOperation, action); action->setText(i18n("Ac&tivities")); //FIXME is that a good string? } void UserActionsMenu::desktopPopupAboutToShow() { if (!m_desktopMenu) return; const VirtualDesktopManager *vds = VirtualDesktopManager::self(); m_desktopMenu->clear(); m_desktopMenu->setPalette(m_client.data()->palette()); QActionGroup *group = new QActionGroup(m_desktopMenu); QAction *action = m_desktopMenu->addAction(i18n("&All Desktops")); action->setData(0); action->setCheckable(true); group->addAction(action); if (!m_client.isNull() && m_client.data()->isOnAllDesktops()) action->setChecked(true); m_desktopMenu->addSeparator(); const uint BASE = 10; for (uint i = 1; i <= vds->count(); ++i) { QString basic_name(QStringLiteral("%1 %2")); if (i < BASE) { basic_name.prepend(QLatin1Char('&')); } action = m_desktopMenu->addAction(basic_name.arg(i).arg(vds->name(i).replace(QLatin1Char('&'), QStringLiteral("&&")))); action->setData(i); action->setCheckable(true); group->addAction(action); if (!m_client.isNull() && !m_client.data()->isOnAllDesktops() && m_client.data()->isOnDesktop(i)) action->setChecked(true); } m_desktopMenu->addSeparator(); action = m_desktopMenu->addAction(i18nc("Create a new desktop and move there the window", "&New Desktop")); action->setData(vds->count() + 1); if (vds->count() >= vds->maximum()) action->setEnabled(false); } void UserActionsMenu::screenPopupAboutToShow() { if (!m_screenMenu) { return; } m_screenMenu->clear(); m_screenMenu->setPalette(m_client.data()->palette()); QActionGroup *group = new QActionGroup(m_screenMenu); for (int i = 0; icount(); ++i) { // assumption: there are not more than 9 screens attached. QAction *action = m_screenMenu->addAction(i18nc("@item:inmenu List of all Screens to send a window to. First argument is a number, second the output identifier. E.g. Screen 1 (HDMI1)", "Screen &%1 (%2)", (i+1), screens()->name(i))); action->setData(i); action->setCheckable(true); if (!m_client.isNull() && i == m_client.data()->screen()) { action->setChecked(true); } group->addAction(action); } } void UserActionsMenu::activityPopupAboutToShow() { if (!m_activityMenu) return; #ifdef KWIN_BUILD_ACTIVITIES if (!Activities::self()) { return; } m_activityMenu->clear(); m_activityMenu->setPalette(m_client.data()->palette()); QAction *action = m_activityMenu->addAction(i18n("&All Activities")); action->setData(QString()); action->setCheckable(true); static QPointer allActivitiesGroup; if (!allActivitiesGroup) { allActivitiesGroup = new QActionGroup(m_activityMenu); } allActivitiesGroup->addAction(action); if (!m_client.isNull() && m_client.data()->isOnAllActivities()) action->setChecked(true); m_activityMenu->addSeparator(); foreach (const QString &id, Activities::self()->running()) { KActivities::Info activity(id); QString name = activity.name(); name.replace('&', "&&"); QWidgetAction *action = new QWidgetAction(m_activityMenu); QCheckBox *box = new QCheckBox(name, m_activityMenu); action->setDefaultWidget(box); const QString icon = activity.icon(); if (!icon.isEmpty()) box->setIcon(QIcon::fromTheme(icon)); box->setBackgroundRole(m_activityMenu->backgroundRole()); box->setForegroundRole(m_activityMenu->foregroundRole()); box->setPalette(m_activityMenu->palette()); connect(box, &QCheckBox::clicked, action, &QAction::triggered); m_activityMenu->addAction(action); action->setData(id); if (!m_client.isNull() && !m_client.data()->isOnAllActivities() && m_client.data()->isOnActivity(id)) box->setChecked(true); } #endif } void UserActionsMenu::slotWindowOperation(QAction *action) { if (!action->data().isValid()) return; Options::WindowOperation op = static_cast< Options::WindowOperation >(action->data().toInt()); QWeakPointer c = (!m_client.isNull()) ? m_client : QWeakPointer(Workspace::self()->activeClient()); if (c.isNull()) return; QString type; switch(op) { case Options::FullScreenOp: if (!c.data()->isFullScreen() && c.data()->userCanSetFullScreen()) type = QStringLiteral("fullscreenaltf3"); break; case Options::NoBorderOp: if (!c.data()->noBorder() && c.data()->userCanSetNoBorder()) type = QStringLiteral("noborderaltf3"); break; default: break; }; if (!type.isEmpty()) helperDialog(type, c); // need to delay performing the window operation as we need to have the // user actions menu closed before we destroy the decoration. Otherwise Qt crashes qRegisterMetaType(); QMetaObject::invokeMethod(workspace(), "performWindowOperation", Qt::QueuedConnection, Q_ARG(KWin::AbstractClient*, c.data()), Q_ARG(Options::WindowOperation, op)); } void UserActionsMenu::slotSendToDesktop(QAction *action) { bool ok = false; uint desk = action->data().toUInt(&ok); if (!ok) { return; } if (m_client.isNull()) return; Workspace *ws = Workspace::self(); VirtualDesktopManager *vds = VirtualDesktopManager::self(); if (desk == 0) { // the 'on_all_desktops' menu entry m_client.data()->setOnAllDesktops(!m_client.data()->isOnAllDesktops()); return; } else if (desk > vds->count()) { vds->setCount(desk); } ws->sendClientToDesktop(m_client.data(), desk, false); } void UserActionsMenu::slotSendToScreen(QAction *action) { const int screen = action->data().toInt(); if (m_client.isNull()) { return; } if (screen >= screens()->count()) { return; } Workspace::self()->sendClientToScreen(m_client.data(), screen); } void UserActionsMenu::slotToggleOnActivity(QAction *action) { #ifdef KWIN_BUILD_ACTIVITIES if (!Activities::self()) { return; } QString activity = action->data().toString(); if (m_client.isNull()) return; if (activity.isEmpty()) { // the 'on_all_activities' menu entry m_client.data()->setOnAllActivities(!m_client.data()->isOnAllActivities()); return; } Client *c = dynamic_cast(m_client.data()); if (!c) { return; } Activities::self()->toggleClientOnActivity(c, activity, false); if (m_activityMenu && m_activityMenu->isVisible() && m_activityMenu->actions().count()) { const bool isOnAll = m_client.data()->isOnAllActivities(); m_activityMenu->actions().at(0)->setChecked(isOnAll); if (isOnAll) { // toggleClientOnActivity interprets "on all" as "on none" and // susequent toggling ("off") would move the client to only that activity. // bug #330838 -> set all but "on all" off to "force proper usage" for (int i = 1; i < m_activityMenu->actions().count(); ++i) { if (QWidgetAction *qwa = qobject_cast(m_activityMenu->actions().at(i))) { if (QCheckBox *qcb = qobject_cast(qwa->defaultWidget())) { qcb->setChecked(false); } } } } } #else Q_UNUSED(action) #endif } //**************************************** // ShortcutDialog //**************************************** ShortcutDialog::ShortcutDialog(const QKeySequence& cut) : _shortcut(cut) { m_ui.setupUi(this); m_ui.keySequenceEdit->setKeySequence(cut); m_ui.warning->hide(); // Listen to changed shortcuts connect(m_ui.keySequenceEdit, &QKeySequenceEdit::editingFinished, this, &ShortcutDialog::keySequenceChanged); connect(m_ui.clearButton, &QToolButton::clicked, [this]{ _shortcut = QKeySequence(); }); m_ui.keySequenceEdit->setFocus(); setWindowFlags(Qt::Popup | Qt::X11BypassWindowManagerHint); } void ShortcutDialog::accept() { QKeySequence seq = shortcut(); if (!seq.isEmpty()) { if (seq[0] == Qt::Key_Escape) { reject(); return; } if (seq[0] == Qt::Key_Space || (seq[0] & Qt::KeyboardModifierMask) == 0) { // clear m_ui.keySequenceEdit->clear(); QDialog::accept(); return; } } QDialog::accept(); } void ShortcutDialog::done(int r) { QDialog::done(r); emit dialogDone(r == Accepted); } void ShortcutDialog::keySequenceChanged() { activateWindow(); // where is the kbd focus lost? cause of popup state? QKeySequence seq = m_ui.keySequenceEdit->keySequence(); if (_shortcut == seq) return; // don't try to update the same if (seq.isEmpty()) { // clear _shortcut = seq; return; } if (seq.count() > 1) { seq = QKeySequence(seq[0]); m_ui.keySequenceEdit->setKeySequence(seq); } // Check if the key sequence is used currently QString sc = seq.toString(); // NOTICE - seq.toString() & the entries in "conflicting" randomly get invalidated after the next call (if no sc has been set & conflicting isn't empty?!) QList conflicting = KGlobalAccel::getGlobalShortcutsByKey(seq); if (!conflicting.isEmpty()) { const KGlobalShortcutInfo &conflict = conflicting.at(0); m_ui.warning->setText(i18nc("'%1' is a keyboard shortcut like 'ctrl+w'", "%1 is already in use", sc)); m_ui.warning->setToolTip(i18nc("keyboard shortcut '%1' is used by action '%2' in application '%3'", "%1 is used by %2 in %3", sc, conflict.friendlyName(), conflict.componentFriendlyName())); m_ui.warning->show(); m_ui.keySequenceEdit->setKeySequence(shortcut()); } else if (seq != _shortcut) { m_ui.warning->hide(); if (QPushButton *ok = m_ui.buttonBox->button(QDialogButtonBox::Ok)) ok->setFocus(); } _shortcut = seq; } QKeySequence ShortcutDialog::shortcut() const { return _shortcut; } //**************************************** // Workspace //**************************************** void Workspace::slotIncreaseWindowOpacity() { if (!active_client) { return; } active_client->setOpacity(qMin(active_client->opacity() + 0.05, 1.0)); } void Workspace::slotLowerWindowOpacity() { if (!active_client) { return; } active_client->setOpacity(qMax(active_client->opacity() - 0.05, 0.05)); } void Workspace::closeActivePopup() { if (active_popup) { active_popup->close(); active_popup = NULL; active_popup_client = NULL; } m_userActionsMenu->close(); } template void Workspace::initShortcut(const QString &actionName, const QString &description, const QKeySequence &shortcut, Slot slot, const QVariant &data) { QAction *a = new QAction(this); a->setProperty("componentName", QStringLiteral(KWIN_NAME)); a->setObjectName(actionName); a->setText(description); if (data.isValid()) { a->setData(data); } KGlobalAccel::self()->setDefaultShortcut(a, QList() << shortcut); KGlobalAccel::self()->setShortcut(a, QList() << shortcut); input()->registerShortcut(shortcut, a, this, slot); } /*! Create the global accel object \c keys. */ void Workspace::initShortcuts() { #define IN_KWIN #include "kwinbindings.cpp" #ifdef KWIN_BUILD_TABBOX TabBox::TabBox::self()->initShortcuts(); #endif VirtualDesktopManager::self()->initShortcuts(); m_userActionsMenu->discard(); // so that it's recreated next time } void Workspace::setupWindowShortcut(AbstractClient* c) { assert(client_keys_dialog == NULL); // TODO: PORT ME (KGlobalAccel related) //keys->setEnabled( false ); //disable_shortcuts_keys->setEnabled( false ); //client_keys->setEnabled( false ); client_keys_dialog = new ShortcutDialog(c->shortcut()); client_keys_client = c; connect(client_keys_dialog, &ShortcutDialog::dialogDone, this, &Workspace::setupWindowShortcutDone); QRect r = clientArea(ScreenArea, c); QSize size = client_keys_dialog->sizeHint(); QPoint pos = c->pos() + c->clientPos(); if (pos.x() + size.width() >= r.right()) pos.setX(r.right() - size.width()); if (pos.y() + size.height() >= r.bottom()) pos.setY(r.bottom() - size.height()); client_keys_dialog->move(pos); client_keys_dialog->show(); active_popup = client_keys_dialog; active_popup_client = c; } void Workspace::setupWindowShortcutDone(bool ok) { // keys->setEnabled( true ); // disable_shortcuts_keys->setEnabled( true ); // client_keys->setEnabled( true ); if (ok) client_keys_client->setShortcut(client_keys_dialog->shortcut().toString()); closeActivePopup(); client_keys_dialog->deleteLater(); client_keys_dialog = NULL; client_keys_client = NULL; if (active_client) active_client->takeFocus(); } void Workspace::clientShortcutUpdated(AbstractClient* c) { QString key = QStringLiteral("_k_session:%1").arg(c->window()); QAction* action = findChild(key); if (!c->shortcut().isEmpty()) { if (action == NULL) { // new shortcut action = new QAction(this); kwinApp()->platform()->setupActionForGlobalAccel(action); action->setProperty("componentName", QStringLiteral(KWIN_NAME)); action->setObjectName(key); action->setText(i18n("Activate Window (%1)", c->caption())); connect(action, &QAction::triggered, c, [c]() { workspace()->activateClient(c, true); } ); } // no autoloading, since it's configured explicitly here and is not meant to be reused // (the key is the window id anyway, which is kind of random) KGlobalAccel::self()->setShortcut(action, QList() << c->shortcut(), KGlobalAccel::NoAutoloading); action->setEnabled(true); } else { KGlobalAccel::self()->removeAllShortcuts(action); delete action; } } void Workspace::performWindowOperation(AbstractClient* c, Options::WindowOperation op) { if (!c) return; if (op == Options::MoveOp || op == Options::UnrestrictedMoveOp) Cursor::setPos(c->geometry().center()); if (op == Options::ResizeOp || op == Options::UnrestrictedResizeOp) Cursor::setPos(c->geometry().bottomRight()); switch(op) { case Options::MoveOp: c->performMouseCommand(Options::MouseMove, Cursor::pos()); break; case Options::UnrestrictedMoveOp: c->performMouseCommand(Options::MouseUnrestrictedMove, Cursor::pos()); break; case Options::ResizeOp: c->performMouseCommand(Options::MouseResize, Cursor::pos()); break; case Options::UnrestrictedResizeOp: c->performMouseCommand(Options::MouseUnrestrictedResize, Cursor::pos()); break; case Options::CloseOp: QMetaObject::invokeMethod(c, "closeWindow", Qt::QueuedConnection); break; case Options::MaximizeOp: c->maximize(c->maximizeMode() == MaximizeFull ? MaximizeRestore : MaximizeFull); break; case Options::HMaximizeOp: c->maximize(c->maximizeMode() ^ MaximizeHorizontal); break; case Options::VMaximizeOp: c->maximize(c->maximizeMode() ^ MaximizeVertical); break; case Options::RestoreOp: c->maximize(MaximizeRestore); break; case Options::MinimizeOp: c->minimize(); break; case Options::ShadeOp: c->performMouseCommand(Options::MouseShade, Cursor::pos()); break; case Options::OnAllDesktopsOp: c->setOnAllDesktops(!c->isOnAllDesktops()); break; case Options::FullScreenOp: c->setFullScreen(!c->isFullScreen(), true); break; case Options::NoBorderOp: c->setNoBorder(!c->noBorder()); break; case Options::KeepAboveOp: { StackingUpdatesBlocker blocker(this); bool was = c->keepAbove(); c->setKeepAbove(!c->keepAbove()); if (was && !c->keepAbove()) raiseClient(c); break; } case Options::KeepBelowOp: { StackingUpdatesBlocker blocker(this); bool was = c->keepBelow(); c->setKeepBelow(!c->keepBelow()); if (was && !c->keepBelow()) lowerClient(c); break; } case Options::OperationsOp: c->performMouseCommand(Options::MouseShade, Cursor::pos()); break; case Options::WindowRulesOp: RuleBook::self()->edit(c, false); break; case Options::ApplicationRulesOp: RuleBook::self()->edit(c, true); break; case Options::SetupWindowShortcutOp: setupWindowShortcut(c); break; case Options::LowerOp: lowerClient(c); break; case Options::TabDragOp: // Handled by decoration itself case Options::NoOp: break; case Options::RemoveTabFromGroupOp: if (c->untab(c->geometry().translated(cascadeOffset(c))) && options->focusPolicyIsReasonable()) takeActivity(c, ActivityFocus | ActivityRaise); break; case Options::ActivateNextTabOp: if (c->tabGroup()) c->tabGroup()->activateNext(); break; case Options::ActivatePreviousTabOp: if (c->tabGroup()) c->tabGroup()->activatePrev(); break; case Options::CloseTabGroupOp: c->tabGroup()->closeAll(); break; } } /** * Called by the decoration in the new API to determine what buttons the user has configured for * window tab dragging and the operations menu. */ Options::WindowOperation Client::mouseButtonToWindowOperation(Qt::MouseButtons button) { Options::MouseCommand com = Options::MouseNothing; bool active = isActive(); if (!wantsInput()) // we cannot be active, use it anyway active = true; if (button == Qt::LeftButton) com = active ? options->commandActiveTitlebar1() : options->commandInactiveTitlebar1(); else if (button == Qt::MidButton) com = active ? options->commandActiveTitlebar2() : options->commandInactiveTitlebar2(); else if (button == Qt::RightButton) com = active ? options->commandActiveTitlebar3() : options->commandInactiveTitlebar3(); // TODO: Complete the list if (com == Options::MouseDragTab) return Options::TabDragOp; if (com == Options::MouseOperationsMenu) return Options::OperationsOp; return Options::NoOp; } /*! Performs a mouse command on this client (see options.h) */ bool Client::performMouseCommand(Options::MouseCommand command, const QPoint &globalPos) { bool replay = false; switch(command) { case Options::MouseShade : toggleShade(); cancelShadeHoverTimer(); break; case Options::MouseSetShade: setShade(ShadeNormal); cancelShadeHoverTimer(); break; case Options::MouseUnsetShade: setShade(ShadeNone); cancelShadeHoverTimer(); break; default: return AbstractClient::performMouseCommand(command, globalPos); } return replay; } void Workspace::slotActivateAttentionWindow() { if (attention_chain.count() > 0) activateClient(attention_chain.first()); } static uint senderValue(QObject *sender) { QAction *act = qobject_cast(sender); bool ok = false; uint i = -1; if (act) i = act->data().toUInt(&ok); if (ok) return i; return -1; } #define USABLE_ACTIVE_CLIENT (active_client && !(active_client->isDesktop() || active_client->isDock())) -void Workspace::slotWindowToDesktop() +void Workspace::slotWindowToDesktop(uint i) { if (USABLE_ACTIVE_CLIENT) { - const uint i = senderValue(sender()); if (i < 1) return; if (i >= 1 && i <= VirtualDesktopManager::self()->count()) sendClientToDesktop(active_client, i, true); } } static bool screenSwitchImpossible() { if (!screens()->isCurrentFollowsMouse()) return false; QStringList args; args << QStringLiteral("--passivepopup") << i18n("The window manager is configured to consider the screen with the mouse on it as active one.\n" "Therefore it is not possible to switch to a screen explicitly.") << QStringLiteral("20"); KProcess::startDetached(QStringLiteral("kdialog"), args); return true; } void Workspace::slotSwitchToScreen() { if (screenSwitchImpossible()) return; const int i = senderValue(sender()); if (i > -1) setCurrentScreen(i); } void Workspace::slotSwitchToNextScreen() { if (screenSwitchImpossible()) return; setCurrentScreen((screens()->current() + 1) % screens()->count()); } void Workspace::slotSwitchToPrevScreen() { if (screenSwitchImpossible()) return; setCurrentScreen((screens()->current() + screens()->count() - 1) % screens()->count()); } void Workspace::slotWindowToScreen() { if (USABLE_ACTIVE_CLIENT) { const int i = senderValue(sender()); if (i < 0) return; if (i >= 0 && i <= screens()->count()) { sendClientToScreen(active_client, i); } } } void Workspace::slotWindowToNextScreen() { if (USABLE_ACTIVE_CLIENT) sendClientToScreen(active_client, (active_client->screen() + 1) % screens()->count()); } void Workspace::slotWindowToPrevScreen() { if (USABLE_ACTIVE_CLIENT) sendClientToScreen(active_client, (active_client->screen() + screens()->count() - 1) % screens()->count()); } /*! Maximizes the popup client */ void Workspace::slotWindowMaximize() { if (USABLE_ACTIVE_CLIENT) performWindowOperation(active_client, Options::MaximizeOp); } /*! Maximizes the popup client vertically */ void Workspace::slotWindowMaximizeVertical() { if (USABLE_ACTIVE_CLIENT) performWindowOperation(active_client, Options::VMaximizeOp); } /*! Maximizes the popup client horiozontally */ void Workspace::slotWindowMaximizeHorizontal() { if (USABLE_ACTIVE_CLIENT) performWindowOperation(active_client, Options::HMaximizeOp); } /*! Minimizes the popup client */ void Workspace::slotWindowMinimize() { if (USABLE_ACTIVE_CLIENT) performWindowOperation(active_client, Options::MinimizeOp); } /*! Shades/unshades the popup client respectively */ void Workspace::slotWindowShade() { if (USABLE_ACTIVE_CLIENT) performWindowOperation(active_client, Options::ShadeOp); } /*! Raises the popup client */ void Workspace::slotWindowRaise() { if (USABLE_ACTIVE_CLIENT) raiseClient(active_client); } /*! Lowers the popup client */ void Workspace::slotWindowLower() { if (USABLE_ACTIVE_CLIENT) { lowerClient(active_client); // As this most likely makes the window no longer visible change the // keyboard focus to the next available window. //activateNextClient( c ); // Doesn't work when we lower a child window if (active_client->isActive() && options->focusPolicyIsReasonable()) { if (options->isNextFocusPrefersMouse()) { AbstractClient *next = clientUnderMouse(active_client->screen()); if (next && next != active_client) requestFocus(next, false); } else { activateClient(topClientOnDesktop(VirtualDesktopManager::self()->current(), -1)); } } } } /*! Does a toggle-raise-and-lower on the popup client; */ void Workspace::slotWindowRaiseOrLower() { if (USABLE_ACTIVE_CLIENT) raiseOrLowerClient(active_client); } void Workspace::slotWindowOnAllDesktops() { if (USABLE_ACTIVE_CLIENT) active_client->setOnAllDesktops(!active_client->isOnAllDesktops()); } void Workspace::slotWindowFullScreen() { if (USABLE_ACTIVE_CLIENT) performWindowOperation(active_client, Options::FullScreenOp); } void Workspace::slotWindowNoBorder() { if (USABLE_ACTIVE_CLIENT) performWindowOperation(active_client, Options::NoBorderOp); } void Workspace::slotWindowAbove() { if (USABLE_ACTIVE_CLIENT) performWindowOperation(active_client, Options::KeepAboveOp); } void Workspace::slotWindowBelow() { if (USABLE_ACTIVE_CLIENT) performWindowOperation(active_client, Options::KeepBelowOp); } void Workspace::slotSetupWindowShortcut() { if (USABLE_ACTIVE_CLIENT) performWindowOperation(active_client, Options::SetupWindowShortcutOp); } /*! Toggles show desktop */ void Workspace::slotToggleShowDesktop() { setShowingDesktop(!showingDesktop()); } template void windowToDesktop(AbstractClient *c) { VirtualDesktopManager *vds = VirtualDesktopManager::self(); Workspace *ws = Workspace::self(); Direction functor; // TODO: why is options->isRollOverDesktops() not honored? const auto desktop = functor(nullptr, true); if (c && !c->isDesktop() && !c->isDock()) { ws->setClientIsMoving(c); vds->setCurrent(desktop); ws->setClientIsMoving(NULL); } } /*! Move window to next desktop */ void Workspace::slotWindowToNextDesktop() { if (USABLE_ACTIVE_CLIENT) windowToNextDesktop(active_client); } void Workspace::windowToNextDesktop(AbstractClient* c) { windowToDesktop(c); } /*! Move window to previous desktop */ void Workspace::slotWindowToPreviousDesktop() { if (USABLE_ACTIVE_CLIENT) windowToPreviousDesktop(active_client); } void Workspace::windowToPreviousDesktop(AbstractClient* c) { windowToDesktop(c); } template void activeClientToDesktop() { VirtualDesktopManager *vds = VirtualDesktopManager::self(); Workspace *ws = Workspace::self(); const int current = vds->current(); Direction functor; const int d = functor(current, options->isRollOverDesktops()); if (d == current) { return; } ws->setClientIsMoving(ws->activeClient()); vds->setCurrent(d); ws->setClientIsMoving(NULL); } void Workspace::slotWindowToDesktopRight() { if (USABLE_ACTIVE_CLIENT) { activeClientToDesktop(); } } void Workspace::slotWindowToDesktopLeft() { if (USABLE_ACTIVE_CLIENT) { activeClientToDesktop(); } } void Workspace::slotWindowToDesktopUp() { if (USABLE_ACTIVE_CLIENT) { activeClientToDesktop(); } } void Workspace::slotWindowToDesktopDown() { if (USABLE_ACTIVE_CLIENT) { activeClientToDesktop(); } } void Workspace::slotActivateNextTab() { if (active_client && active_client->tabGroup()) active_client->tabGroup()->activateNext(); } void Workspace::slotActivatePrevTab() { if (active_client && active_client->tabGroup()) active_client->tabGroup()->activatePrev(); } void Workspace::slotUntab() { if (active_client) active_client->untab(active_client->geometry().translated(cascadeOffset(active_client))); } /*! Kill Window feature, similar to xkill */ void Workspace::slotKillWindow() { if (m_windowKiller.isNull()) { m_windowKiller.reset(new KillWindow()); } m_windowKiller->start(); } /*! Switches to the nearest window in given direction */ void Workspace::switchWindow(Direction direction) { if (!active_client) return; AbstractClient *c = active_client; int desktopNumber = c->isOnAllDesktops() ? VirtualDesktopManager::self()->current() : c->desktop(); // Centre of the active window QPoint curPos(c->pos().x() + c->geometry().width() / 2, c->pos().y() + c->geometry().height() / 2); if (!switchWindow(c, direction, curPos, desktopNumber)) { auto opposite = [&] { switch(direction) { case DirectionNorth: return QPoint(curPos.x(), screens()->geometry().height()); case DirectionSouth: return QPoint(curPos.x(), 0); case DirectionEast: return QPoint(0, curPos.y()); case DirectionWest: return QPoint(screens()->geometry().width(), curPos.y()); default: Q_UNREACHABLE(); } }; switchWindow(c, direction, opposite(), desktopNumber); } } bool Workspace::switchWindow(AbstractClient *c, Direction direction, QPoint curPos, int d) { AbstractClient *switchTo = nullptr; int bestScore = 0; ToplevelList clist = stackingOrder(); for (ToplevelList::Iterator i = clist.begin(); i != clist.end(); ++i) { auto client = qobject_cast(*i); if (!client) { continue; } if (client->wantsTabFocus() && *i != c && client->isOnDesktop(d) && !client->isMinimized() && (*i)->isOnCurrentActivity()) { // Centre of the other window QPoint other(client->pos().x() + client->geometry().width() / 2, client->pos().y() + client->geometry().height() / 2); int distance; int offset; switch(direction) { case DirectionNorth: distance = curPos.y() - other.y(); offset = qAbs(other.x() - curPos.x()); break; case DirectionEast: distance = other.x() - curPos.x(); offset = qAbs(other.y() - curPos.y()); break; case DirectionSouth: distance = other.y() - curPos.y(); offset = qAbs(other.x() - curPos.x()); break; case DirectionWest: distance = curPos.x() - other.x(); offset = qAbs(other.y() - curPos.y()); break; default: distance = -1; offset = -1; } if (distance > 0) { // Inverse score int score = distance + offset + ((offset * offset) / distance); if (score < bestScore || !switchTo) { switchTo = client; bestScore = score; } } } } if (switchTo) { if (switchTo->tabGroup()) switchTo = switchTo->tabGroup()->current(); activateClient(switchTo); } return switchTo; } /*! Shows the window operations popup menu for the activeClient() */ void Workspace::slotWindowOperations() { if (!active_client) return; QPoint pos = active_client->pos() + active_client->clientPos(); showWindowMenu(QRect(pos, pos), active_client); } void Workspace::showWindowMenu(const QRect &pos, AbstractClient* cl) { m_userActionsMenu->show(pos, cl); } void Workspace::showApplicationMenu(const QRect &pos, AbstractClient *c, int actionId) { ApplicationMenu::self()->showApplicationMenu(c->geometry().topLeft() + pos.bottomLeft(), c, actionId); } /*! Closes the popup client */ void Workspace::slotWindowClose() { // TODO: why? // if ( tab_box->isVisible()) // return; if (USABLE_ACTIVE_CLIENT) performWindowOperation(active_client, Options::CloseOp); } /*! Starts keyboard move mode for the popup client */ void Workspace::slotWindowMove() { if (USABLE_ACTIVE_CLIENT) performWindowOperation(active_client, Options::UnrestrictedMoveOp); } /*! Starts keyboard resize mode for the popup client */ void Workspace::slotWindowResize() { if (USABLE_ACTIVE_CLIENT) performWindowOperation(active_client, Options::UnrestrictedResizeOp); } void Workspace::slotInvertScreen() { using namespace Xcb::RandR; bool succeeded = false; if (Xcb::Extensions::self()->isRandrAvailable()) { ScreenResources res((active_client && active_client->window() != XCB_WINDOW_NONE) ? active_client->window() : rootWindow()); if (!res.isNull()) { for (int j = 0; j < res->num_crtcs; ++j) { auto crtc = res.crtcs()[j]; CrtcGamma gamma(crtc); if (gamma.isNull()) { continue; } if (gamma->size) { qCDebug(KWIN_CORE) << "inverting screen using xcb_randr_set_crtc_gamma"; const int half = gamma->size / 2 + 1; uint16_t *red = gamma.red(); uint16_t *green = gamma.green(); uint16_t *blue = gamma.blue(); for (int i = 0; i < half; ++i) { auto invert = [&gamma, i](uint16_t *ramp) { qSwap(ramp[i], ramp[gamma->size - 1 - i]); }; invert(red); invert(green); invert(blue); } xcb_randr_set_crtc_gamma(connection(), crtc, gamma->size, red, green, blue); succeeded = true; } } } } if (succeeded) return; //BEGIN effect plugin inversion - atm only works with OpenGL and has an overhead to it if (effects) { if (Effect *inverter = static_cast(effects)->provides(Effect::ScreenInversion)) { qCDebug(KWIN_CORE) << "inverting screen using Effect plugin"; QMetaObject::invokeMethod(inverter, "toggleScreenInversion", Qt::DirectConnection); } } if (!succeeded) qCDebug(KWIN_CORE) << "sorry - neither Xrandr, nor XF86VidModeSetGammaRamp worked and there's no inversion supplying effect plugin either"; } #undef USABLE_ACTIVE_CLIENT void AbstractClient::setShortcut(const QString& _cut) { QString cut = rules()->checkShortcut(_cut); auto updateShortcut = [this](const QKeySequence &cut = QKeySequence()) { if (_shortcut == cut) return; _shortcut = cut; setShortcutInternal(); }; if (cut.isEmpty()) { updateShortcut(); return; } if (cut == shortcut().toString()) { return; // no change } // Format: // base+(abcdef)base+(abcdef) // E.g. Alt+Ctrl+(ABCDEF);Meta+X,Meta+(ABCDEF) if (!cut.contains(QLatin1Char('(')) && !cut.contains(QLatin1Char(')')) && !cut.contains(QLatin1String(" - "))) { if (workspace()->shortcutAvailable(cut, this)) updateShortcut(QKeySequence(cut)); else updateShortcut(); return; } QList< QKeySequence > keys; QStringList groups = cut.split(QStringLiteral(" - ")); for (QStringList::ConstIterator it = groups.constBegin(); it != groups.constEnd(); ++it) { QRegExp reg(QStringLiteral("(.*\\+)\\((.*)\\)")); if (reg.indexIn(*it) > -1) { QString base = reg.cap(1); QString list = reg.cap(2); for (int i = 0; i < list.length(); ++i) { QKeySequence c(base + list[ i ]); if (!c.isEmpty()) keys.append(c); } } else { // regexp doesn't match, so it should be a normal shortcut QKeySequence c(*it); if (!c.isEmpty()) { keys.append(c); } } } for (auto it = keys.constBegin(); it != keys.constEnd(); ++it) { if (_shortcut == *it) // current one is in the list return; } for (auto it = keys.constBegin(); it != keys.constEnd(); ++it) { if (workspace()->shortcutAvailable(*it, this)) { updateShortcut(*it); return; } } updateShortcut(); } void AbstractClient::setShortcutInternal() { workspace()->clientShortcutUpdated(this); } void Client::setShortcutInternal() { updateCaption(); #if 0 workspace()->clientShortcutUpdated(this); #else // Workaround for kwin<->kglobalaccel deadlock, when KWin has X grab and the kded // kglobalaccel module tries to create the key grab. KWin should preferably grab // they keys itself anyway :(. QTimer::singleShot(0, this, SLOT(delayedSetShortcut())); #endif } void Client::delayedSetShortcut() { workspace()->clientShortcutUpdated(this); } bool Workspace::shortcutAvailable(const QKeySequence &cut, AbstractClient* ignore) const { if (ignore && cut == ignore->shortcut()) return true; if (!KGlobalAccel::getGlobalShortcutsByKey(cut).isEmpty()) { return false; } for (auto it = m_allClients.constBegin(); it != m_allClients.constEnd(); ++it) { if ((*it) != ignore && (*it)->shortcut() == cut) return false; } return true; } } // namespace diff --git a/workspace.h b/workspace.h index 0b3ac42d8..203004306 100644 --- a/workspace.h +++ b/workspace.h @@ -1,768 +1,768 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 1999, 2000 Matthias Ettrich Copyright (C) 2003 Lubos Lunak Copyright (C) 2009 Lucas Murray This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #ifndef KWIN_WORKSPACE_H #define KWIN_WORKSPACE_H // kwin #include "sm.h" #include "options.h" #include "utils.h" // Qt #include #include // std #include #include // TODO: Cleanup the order of things in this .h file class QStringList; class KConfig; class KConfigGroup; class KStartupInfo; class KStartupInfoId; class KStartupInfoData; namespace KWin { namespace Xcb { class Tree; class Window; } class AbstractClient; class Client; class KillWindow; class ShortcutDialog; class UserActionsMenu; class Compositor; class X11EventFilter; enum class Predicate; class KWIN_EXPORT Workspace : public QObject { Q_OBJECT public: explicit Workspace(const QString &sessionKey = QString()); virtual ~Workspace(); static Workspace* self() { return _self; } bool workspaceEvent(xcb_generic_event_t*); bool workspaceEvent(QEvent*); bool hasClient(const Client*); bool hasClient(const AbstractClient*); /** * @brief Finds the first Client matching the condition expressed by passed in @p func. * * Internally findClient uses the std::find_if algorithm and that determines how the function * needs to be implemented. An example usage for finding a Client with a matching windowId * @code * xcb_window_t w; // our test window * Client *client = findClient([w](const Client *c) -> bool { * return c->window() == w; * }); * @endcode * * For the standard cases of matching the window id with one of the Client's windows use * the simplified overload method findClient(Predicate, xcb_window_t). Above example * can be simplified to: * @code * xcb_window_t w; // our test window * Client *client = findClient(Predicate::WindowMatch, w); * @endcode * * @param func Unary function that accepts a Client* as argument and * returns a value convertible to bool. The value returned indicates whether the * Client* is considered a match in the context of this function. * The function shall not modify its argument. * This can either be a function pointer or a function object. * @return KWin::Client* The found Client or @c null * @see findClient(Predicate, xcb_window_t) */ Client *findClient(std::function func) const; AbstractClient *findAbstractClient(std::function func) const; /** * @brief Finds the Client matching the given match @p predicate for the given window. * * @param predicate Which window should be compared * @param w The window id to test against * @return KWin::Client* The found Client or @c null * @see findClient(std::function) */ Client *findClient(Predicate predicate, xcb_window_t w) const; void forEachClient(std::function func); void forEachAbstractClient(std::function func); Unmanaged *findUnmanaged(std::function func) const; /** * @brief Finds the Unmanaged with the given window id. * * @param w The window id to search for * @return KWin::Unmanaged* Found Unmanaged or @c null if there is no Unmanaged with given Id. */ Unmanaged *findUnmanaged(xcb_window_t w) const; void forEachUnmanaged(std::function func); Toplevel *findToplevel(std::function func) const; /** * Finds the Toplevel for the KWin internal window @p w. * On Wayland this is normally a ShellClient. For X11 an Unmanaged. **/ Toplevel *findToplevel(QWindow *w) const; /** * @brief Finds a Toplevel for the internal window @p w. * * Internal window means a window created by KWin itself. On X11 this is an Unmanaged * and mapped by the window id, on Wayland a ShellClient mapped on the internal window id. * * @returns Toplevel **/ Toplevel *findInternal(QWindow *w) const; QRect clientArea(clientAreaOption, const QPoint& p, int desktop) const; QRect clientArea(clientAreaOption, const AbstractClient* c) const; QRect clientArea(clientAreaOption, int screen, int desktop) const; QRegion restrictedMoveArea(int desktop, StrutAreas areas = StrutAreaAll) const; bool initializing() const; /** * Returns the active client, i.e. the client that has the focus (or None * if no client has the focus) */ AbstractClient* activeClient() const; /** * Client that was activated, but it's not yet really activeClient(), because * we didn't process yet the matching FocusIn event. Used mostly in focus * stealing prevention code. */ AbstractClient* mostRecentlyActivatedClient() const; AbstractClient* clientUnderMouse(int screen) const; void activateClient(AbstractClient*, bool force = false); void requestFocus(AbstractClient* c, bool force = false); enum ActivityFlag { ActivityFocus = 1 << 0, // focus the window ActivityFocusForce = 1 << 1 | ActivityFocus, // focus even if Dock etc. ActivityRaise = 1 << 2 // raise the window }; Q_DECLARE_FLAGS(ActivityFlags, ActivityFlag) void takeActivity(AbstractClient* c, ActivityFlags flags); bool allowClientActivation(const AbstractClient* c, xcb_timestamp_t time = -1U, bool focus_in = false, bool ignore_desktop = false); void restoreFocus(); void gotFocusIn(const AbstractClient*); void setShouldGetFocus(AbstractClient*); bool activateNextClient(AbstractClient* c); bool focusChangeEnabled() { return block_focus == 0; } /** * Indicates that the client c is being moved around by the user. */ void setClientIsMoving(AbstractClient* c); QPoint adjustClientPosition(AbstractClient* c, QPoint pos, bool unrestricted, double snapAdjust = 1.0); QRect adjustClientSize(AbstractClient* c, QRect moveResizeGeom, int mode); void raiseClient(AbstractClient* c, bool nogroup = false); void lowerClient(AbstractClient* c, bool nogroup = false); void raiseClientRequest(AbstractClient* c, NET::RequestSource src = NET::FromApplication, xcb_timestamp_t timestamp = 0); void lowerClientRequest(Client* c, NET::RequestSource src, xcb_timestamp_t timestamp); void lowerClientRequest(AbstractClient* c); void restackClientUnderActive(AbstractClient*); void restack(AbstractClient *c, AbstractClient *under, bool force = false); void updateClientLayer(AbstractClient* c); void raiseOrLowerClient(AbstractClient*); void resetUpdateToolWindowsTimer(); void restoreSessionStackingOrder(Client* c); void updateStackingOrder(bool propagate_new_clients = false); void forceRestacking(); void clientHidden(AbstractClient*); void clientAttentionChanged(AbstractClient* c, bool set); /** * @return List of clients currently managed by Workspace **/ const ClientList &clientList() const { return clients; } /** * @return List of unmanaged "clients" currently registered in Workspace **/ const UnmanagedList &unmanagedList() const { return unmanaged; } /** * @return List of desktop "clients" currently managed by Workspace **/ const ClientList &desktopList() const { return desktops; } /** * @return List of deleted "clients" currently managed by Workspace **/ const DeletedList &deletedList() const { return deleted; } /** * @returns List of all clients (either X11 or Wayland) currently managed by Workspace **/ const QList allClientList() const { return m_allClients; } void stackScreenEdgesUnderOverrideRedirect(); public: QPoint cascadeOffset(const AbstractClient *c) const; private: Compositor *m_compositor; //------------------------------------------------- // Unsorted public: bool isOnCurrentHead(); // True when performing Workspace::updateClientArea(). // The calls below are valid only in that case. bool inUpdateClientArea() const; QRegion previousRestrictedMoveArea(int desktop, StrutAreas areas = StrutAreaAll) const; QVector< QRect > previousScreenSizes() const; int oldDisplayWidth() const; int oldDisplayHeight() const; /** * Returns the list of clients sorted in stacking order, with topmost client * at the last position */ const ToplevelList& stackingOrder() const; ToplevelList xStackingOrder() const; ClientList ensureStackingOrder(const ClientList& clients) const; QList ensureStackingOrder(const QList &clients) const; AbstractClient* topClientOnDesktop(int desktop, int screen, bool unconstrained = false, bool only_normal = true) const; AbstractClient* findDesktop(bool topmost, int desktop) const; void sendClientToDesktop(AbstractClient* c, int desktop, bool dont_activate); void windowToPreviousDesktop(AbstractClient* c); void windowToNextDesktop(AbstractClient* c); void sendClientToScreen(AbstractClient* c, int screen); /** * Shows the menu operations menu for the client and makes it active if * it's not already. */ void showWindowMenu(const QRect& pos, AbstractClient* cl); const UserActionsMenu *userActionsMenu() const { return m_userActionsMenu; } void showApplicationMenu(const QRect &pos, AbstractClient *c, int actionId); void updateMinimizedOfTransients(AbstractClient*); void updateOnAllDesktopsOfTransients(AbstractClient*); void checkTransients(xcb_window_t w); void storeSession(KConfig* config, SMSavePhase phase); void storeClient(KConfigGroup &cg, int num, Client *c); void storeSubSession(const QString &name, QSet sessionIds); void loadSubSessionInfo(const QString &name); SessionInfo* takeSessionInfo(Client*); // D-Bus interface bool waitForCompositingSetup(); QString supportInformation() const; void setCurrentScreen(int new_screen); void setShowingDesktop(bool showing); bool showingDesktop() const; void sendPingToWindow(xcb_window_t w, xcb_timestamp_t timestamp); // Called from Client::pingWindow() void removeClient(Client*); // Only called from Client::destroyClient() or Client::releaseWindow() void setActiveClient(AbstractClient*); Group* findGroup(xcb_window_t leader) const; void addGroup(Group* group); void removeGroup(Group* group); Group* findClientLeaderGroup(const Client* c) const; void removeUnmanaged(Unmanaged*); // Only called from Unmanaged::release() void removeDeleted(Deleted*); void addDeleted(Deleted*, Toplevel*); bool checkStartupNotification(xcb_window_t w, KStartupInfoId& id, KStartupInfoData& data); void focusToNull(); // SELI TODO: Public? void clientShortcutUpdated(AbstractClient* c); bool shortcutAvailable(const QKeySequence &cut, AbstractClient* ignore = NULL) const; bool globalShortcutsDisabled() const; void disableGlobalShortcutsForClient(bool disable); void sessionSaveStarted(); void sessionSaveDone(); void setWasUserInteraction(); bool wasUserInteraction() const; bool sessionSaving() const; int packPositionLeft(const AbstractClient* cl, int oldx, bool left_edge) const; int packPositionRight(const AbstractClient* cl, int oldx, bool right_edge) const; int packPositionUp(const AbstractClient* cl, int oldy, bool top_edge) const; int packPositionDown(const AbstractClient* cl, int oldy, bool bottom_edge) const; void cancelDelayFocus(); void requestDelayFocus(AbstractClient*); /** * updates the mouse position to track whether a focus follow mouse focus change was caused by * an actual mouse move * is esp. called on enter/motion events of inactive windows * since an active window doesn't receive mouse events, it must also be invoked if a (potentially) * active window might be moved/resize away from the cursor (causing a leave event) */ void updateFocusMousePosition(const QPoint& pos); QPoint focusMousePosition() const; AbstractClient* getMovingClient() { return movingClient; } /** * @returns Whether we have a Compositor and it is active (Scene created) **/ bool compositing() const; void registerEventFilter(X11EventFilter *filter); void unregisterEventFilter(X11EventFilter *filter); void markXStackingOrderAsDirty(); void quickTileWindow(QuickTileMode mode); enum Direction { DirectionNorth, DirectionEast, DirectionSouth, DirectionWest }; void switchWindow(Direction direction); public Q_SLOTS: void performWindowOperation(KWin::AbstractClient* c, Options::WindowOperation op); // Keybindings //void slotSwitchToWindow( int ); - void slotWindowToDesktop(); + void slotWindowToDesktop(uint i); //void slotWindowToListPosition( int ); void slotSwitchToScreen(); void slotWindowToScreen(); void slotSwitchToNextScreen(); void slotWindowToNextScreen(); void slotSwitchToPrevScreen(); void slotWindowToPrevScreen(); void slotToggleShowDesktop(); void slotWindowMaximize(); void slotWindowMaximizeVertical(); void slotWindowMaximizeHorizontal(); void slotWindowMinimize(); void slotWindowShade(); void slotWindowRaise(); void slotWindowLower(); void slotWindowRaiseOrLower(); void slotActivateAttentionWindow(); void slotWindowPackLeft(); void slotWindowPackRight(); void slotWindowPackUp(); void slotWindowPackDown(); void slotWindowGrowHorizontal(); void slotWindowGrowVertical(); void slotWindowShrinkHorizontal(); void slotWindowShrinkVertical(); void slotIncreaseWindowOpacity(); void slotLowerWindowOpacity(); void slotWindowOperations(); void slotWindowClose(); void slotWindowMove(); void slotWindowResize(); void slotWindowAbove(); void slotWindowBelow(); void slotWindowOnAllDesktops(); void slotWindowFullScreen(); void slotWindowNoBorder(); void slotWindowToNextDesktop(); void slotWindowToPreviousDesktop(); void slotWindowToDesktopRight(); void slotWindowToDesktopLeft(); void slotWindowToDesktopUp(); void slotWindowToDesktopDown(); void reconfigure(); void slotReconfigure(); void slotKillWindow(); void slotSetupWindowShortcut(); void setupWindowShortcutDone(bool); void slotToggleCompositing(); void slotInvertScreen(); void updateClientArea(); void slotActivateNextTab(); // Slot to move left the active Client. void slotActivatePrevTab(); // Slot to move right the active Client. void slotUntab(); // Slot to remove the active client from its group. private Q_SLOTS: void desktopResized(); void selectWmInputEventMask(); void slotUpdateToolWindows(); void delayFocus(); void slotReloadConfig(); void updateCurrentActivity(const QString &new_activity); // virtual desktop handling void moveClientsFromRemovedDesktops(); void slotDesktopCountChanged(uint previousCount, uint newCount); void slotCurrentDesktopChanged(uint oldDesktop, uint newDesktop); // session management void saveState(QSessionManager &sm); void commitData(QSessionManager &sm); Q_SIGNALS: /** * Emitted after the Workspace has setup the complete initialization process. * This can be used to connect to for performing post-workspace initialization. **/ void workspaceInitialized(); //Signals required for the scripting interface void desktopPresenceChanged(KWin::AbstractClient*, int); void currentDesktopChanged(int, KWin::AbstractClient*); void clientAdded(KWin::Client*); void clientRemoved(KWin::AbstractClient*); void clientActivated(KWin::AbstractClient*); void clientDemandsAttentionChanged(KWin::AbstractClient*, bool); void groupAdded(KWin::Group*); void unmanagedAdded(KWin::Unmanaged*); void unmanagedRemoved(KWin::Unmanaged*); void deletedRemoved(KWin::Deleted*); void propertyNotify(long a); void configChanged(); void reinitializeCompositing(); void showingDesktopChanged(bool showing); /** * This signels is emitted when ever the stacking order is change, ie. a window is risen * or lowered */ void stackingOrderChanged(); private: void init(); void initShortcuts(); template void initShortcut(const QString &actionName, const QString &description, const QKeySequence &shortcut, Slot slot, const QVariant &data = QVariant()); void setupWindowShortcut(AbstractClient* c); bool switchWindow(AbstractClient *c, Direction direction, QPoint curPos, int desktop); void propagateClients(bool propagate_new_clients); // Called only from updateStackingOrder ToplevelList constrainedStackingOrder(); void raiseClientWithinApplication(AbstractClient* c); void lowerClientWithinApplication(AbstractClient* c); bool allowFullClientRaising(const AbstractClient* c, xcb_timestamp_t timestamp); bool keepTransientAbove(const AbstractClient* mainwindow, const AbstractClient* transient); void blockStackingUpdates(bool block); void updateToolWindows(bool also_hide); void fixPositionAfterCrash(xcb_window_t w, const xcb_get_geometry_reply_t *geom); void saveOldScreenSizes(); /// This is the right way to create a new client Client* createClient(xcb_window_t w, bool is_mapped); void setupClientConnections(AbstractClient *client); void addClient(Client* c); Unmanaged* createUnmanaged(xcb_window_t w); void addUnmanaged(Unmanaged* c); //--------------------------------------------------------------------- void closeActivePopup(); void updateClientArea(bool force); void resetClientAreas(uint desktopCount); void updateClientVisibilityOnDesktopChange(uint oldDesktop, uint newDesktop); void activateClientOnNewDesktop(uint desktop); AbstractClient *findClientToActivateOnDesktop(uint desktop); QWidget* active_popup; AbstractClient* active_popup_client; int m_initialDesktop; void loadSessionInfo(const QString &key); void addSessionInfo(KConfigGroup &cg); QList session; static const char* windowTypeToTxt(NET::WindowType type); static NET::WindowType txtToWindowType(const char* txt); static bool sessionInfoWindowTypeMatch(Client* c, SessionInfo* info); void updateXStackingOrder(); AbstractClient* active_client; AbstractClient* last_active_client; AbstractClient* most_recently_raised; // Used ONLY by raiseOrLowerClient() AbstractClient* movingClient; // Delay(ed) window focus timer and client QTimer* delayFocusTimer; AbstractClient* delayfocus_client; QPoint focusMousePos; ClientList clients; QList m_allClients; ClientList desktops; UnmanagedList unmanaged; DeletedList deleted; ToplevelList unconstrained_stacking_order; // Topmost last ToplevelList stacking_order; // Topmost last bool force_restacking; ToplevelList x_stacking; // From XQueryTree() std::unique_ptr m_xStackingQueryTree; QList should_get_focus; // Last is most recent QList attention_chain; bool showing_desktop; GroupList groups; bool was_user_interaction; bool session_saving; int session_active_client; int session_desktop; int block_focus; /** * Holds the menu containing the user actions which is shown * on e.g. right click the window decoration. **/ UserActionsMenu *m_userActionsMenu; void modalActionsSwitch(bool enabled); ShortcutDialog* client_keys_dialog; AbstractClient* client_keys_client; bool global_shortcuts_disabled_for_client; // Timer to collect requests for 'reconfigure' QTimer reconfigureTimer; QTimer updateToolWindowsTimer; static Workspace* _self; bool workspaceInit; KStartupInfo* startup; QVector workarea; // Array of workareas for virtual desktops // Array of restricted areas that window cannot be moved into QVector restrictedmovearea; // Array of the previous restricted areas that window cannot be moved into QVector oldrestrictedmovearea; QVector< QVector > screenarea; // Array of workareas per xinerama screen for all virtual desktops QVector< QRect > oldscreensizes; // array of previous sizes of xinerama screens QSize olddisplaysize; // previous sizes od displayWidth()/displayHeight() int set_active_client_recursion; int block_stacking_updates; // When > 0, stacking updates are temporarily disabled bool blocked_propagating_new_clients; // Propagate also new clients after enabling stacking updates? QScopedPointer m_nullFocus; friend class StackingUpdatesBlocker; QScopedPointer m_windowKiller; QList m_eventFilters; QList m_genericEventFilters; private: friend bool performTransiencyCheck(); friend Workspace *workspace(); }; /** * Helper for Workspace::blockStackingUpdates() being called in pairs (True/false) */ class StackingUpdatesBlocker { public: explicit StackingUpdatesBlocker(Workspace* w) : ws(w) { ws->blockStackingUpdates(true); } ~StackingUpdatesBlocker() { ws->blockStackingUpdates(false); } private: Workspace* ws; }; class ColorMapper : public QObject { Q_OBJECT public: ColorMapper(QObject *parent); virtual ~ColorMapper(); public Q_SLOTS: void update(); private: xcb_colormap_t m_default; xcb_colormap_t m_installed; }; //--------------------------------------------------------- // Unsorted inline bool Workspace::initializing() const { return workspaceInit; } inline AbstractClient *Workspace::activeClient() const { return active_client; } inline AbstractClient *Workspace::mostRecentlyActivatedClient() const { return should_get_focus.count() > 0 ? should_get_focus.last() : active_client; } inline void Workspace::addGroup(Group* group) { emit groupAdded(group); groups.append(group); } inline void Workspace::removeGroup(Group* group) { groups.removeAll(group); } inline const ToplevelList& Workspace::stackingOrder() const { // TODO: Q_ASSERT( block_stacking_updates == 0 ); return stacking_order; } inline void Workspace::setWasUserInteraction() { was_user_interaction = true; } inline bool Workspace::wasUserInteraction() const { return was_user_interaction; } inline void Workspace::sessionSaveStarted() { session_saving = true; } inline bool Workspace::sessionSaving() const { return session_saving; } inline bool Workspace::showingDesktop() const { return showing_desktop; } inline bool Workspace::globalShortcutsDisabled() const { return global_shortcuts_disabled_for_client; } inline void Workspace::forceRestacking() { force_restacking = true; StackingUpdatesBlocker blocker(this); // Do restacking if not blocked } inline void Workspace::updateFocusMousePosition(const QPoint& pos) { focusMousePos = pos; } inline QPoint Workspace::focusMousePosition() const { return focusMousePos; } inline void Workspace::forEachClient(std::function< void (Client*) > func) { std::for_each(clients.constBegin(), clients.constEnd(), func); std::for_each(desktops.constBegin(), desktops.constEnd(), func); } inline void Workspace::forEachUnmanaged(std::function< void (Unmanaged*) > func) { std::for_each(unmanaged.constBegin(), unmanaged.constEnd(), func); } inline bool Workspace::hasClient(const Client* c) { return findClient([c](const Client *test) { return test == c; }); } inline Workspace *workspace() { return Workspace::_self; } } // namespace Q_DECLARE_OPERATORS_FOR_FLAGS(KWin::Workspace::ActivityFlags) #endif