diff --git a/killwindow.cpp b/killwindow.cpp index e2650a81cf..3f9ab7c93e 100644 --- a/killwindow.cpp +++ b/killwindow.cpp @@ -21,31 +21,14 @@ along with this program. If not, see . *********************************************************************/ #include "killwindow.h" #include "client.h" -#include "cursor.h" -#include "workspace.h" -#include "xcbutils.h" -// XLib -#include -#include -#include -// XCB -#include +#include "main.h" +#include "platform.h" +#include "unmanaged.h" namespace KWin { KillWindow::KillWindow() - : X11EventFilter(QVector{XCB_BUTTON_PRESS, - XCB_BUTTON_RELEASE, - XCB_MOTION_NOTIFY, - XCB_ENTER_NOTIFY, - XCB_LEAVE_NOTIFY, - XCB_KEY_PRESS, - XCB_KEY_RELEASE, - XCB_FOCUS_IN, - XCB_FOCUS_OUT - }) - , m_active(false) { } @@ -55,156 +38,18 @@ KillWindow::~KillWindow() void KillWindow::start() { - static xcb_cursor_t kill_cursor = XCB_CURSOR_NONE; - if (kill_cursor == XCB_CURSOR_NONE) { - kill_cursor = createCursor(); - } - if (m_active) { - return; - } - - xcb_connection_t *c = connection(); - ScopedCPointer grabPointer(xcb_grab_pointer_reply(c, xcb_grab_pointer_unchecked(c, false, rootWindow(), - XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE | - XCB_EVENT_MASK_POINTER_MOTION | - XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW, - XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, XCB_WINDOW_NONE, - kill_cursor, XCB_TIME_CURRENT_TIME), NULL)); - if (grabPointer.isNull() || grabPointer->status != XCB_GRAB_STATUS_SUCCESS) { - return; - } - m_active = grabXKeyboard(); - if (!m_active) { - xcb_ungrab_pointer(connection(), XCB_TIME_CURRENT_TIME); - return; - } - grabXServer(); -} - -xcb_cursor_t KillWindow::createCursor() -{ - xcb_cursor_t cursor = Cursor::x11Cursor(QByteArrayLiteral("pirate")); - if (cursor != XCB_CURSOR_NONE) { - return cursor; - } - // fallback on font - xcb_connection_t *c = connection(); - const xcb_font_t cursorFont = xcb_generate_id(c); - xcb_open_font(c, cursorFont, strlen ("cursor"), "cursor"); - cursor = xcb_generate_id(c); - xcb_create_glyph_cursor(c, cursor, cursorFont, cursorFont, - XC_pirate, /* source character glyph */ - XC_pirate + 1, /* mask character glyph */ - 0, 0, 0, 0, 0, 0); /* r b g r b g */ - return cursor; -} - -void KillWindow::processEvent(xcb_generic_event_t *event) -{ - if (event->response_type == XCB_BUTTON_RELEASE) { - xcb_button_release_event_t *buttonEvent = reinterpret_cast(event); - handleButtonRelease(buttonEvent->detail, buttonEvent->child); - } else if (event->response_type == XCB_KEY_PRESS) { - xcb_key_press_event_t *keyEvent = reinterpret_cast(event); - handleKeyPress(keyEvent->detail, keyEvent->state); - } -} - -bool KillWindow::event(xcb_generic_event_t *event) -{ - if (!m_active) { - return false; - } - processEvent(event); - - return true; -} - -void KillWindow::handleButtonRelease(xcb_button_t button, xcb_window_t window) -{ - if (button == XCB_BUTTON_INDEX_3) { - release(); - return; - } - if (button == XCB_BUTTON_INDEX_1 || button == XCB_BUTTON_INDEX_2) { - killWindowId(window); - release(); - return; - } -} - -void KillWindow::handleKeyPress(xcb_keycode_t keycode, uint16_t state) -{ - xcb_key_symbols_t *symbols = xcb_key_symbols_alloc(connection()); - xcb_keysym_t kc = xcb_key_symbols_get_keysym(symbols, keycode, 0); - int mx = 0; - int my = 0; - const bool returnPressed = (kc == XK_Return) || (kc == XK_space); - const bool escapePressed = (kc == XK_Escape); - if (kc == XK_Left) { - mx = -10; - } - if (kc == XK_Right) { - mx = 10; - } - if (kc == XK_Up) { - my = -10; - } - if (kc == XK_Down) { - my = 10; - } - if (state & XCB_MOD_MASK_CONTROL) { - mx /= 10; - my /= 10; - } - Cursor::setPos(Cursor::pos() + QPoint(mx, my)); - if (returnPressed) { - performKill(); - } - if (returnPressed || escapePressed) { - release(); - } - xcb_key_symbols_free(symbols); -} - -void KillWindow::performKill() -{ - Xcb::Pointer pointer(rootWindow()); - if (!pointer.isNull() && pointer->child != XCB_WINDOW_NONE) { - killWindowId(pointer->child); - } -} - -void KillWindow::release() -{ - ungrabXKeyboard(); - xcb_ungrab_pointer(connection(), XCB_TIME_CURRENT_TIME); - ungrabXServer(); - m_active = false; -} - -void KillWindow::killWindowId(xcb_window_t window_to_kill) -{ - if (window_to_kill == XCB_WINDOW_NONE) - return; - xcb_window_t window = window_to_kill; - Client* client = NULL; - while (true) { - client = Workspace::self()->findClient(Predicate::FrameIdMatch, window); - if (client) { - break; // Found the client - } - Xcb::Tree tree(window); - if (window == tree->root) { - // We didn't find the client, probably an override-redirect window - break; - } - window = tree->parent; // Go up - } - if (client) - client->killWindow(); - else - xcb_kill_client(connection(), window_to_kill); + kwinApp()->platform()->startInteractiveWindowSelection( + [] (KWin::Toplevel *t) { + if (!t) { + return; + } + if (Client *c = qobject_cast(t)) { + c->killWindow(); + } else if (Unmanaged *u = qobject_cast(t)) { + xcb_kill_client(connection(), u->window()); + } + }, QByteArrayLiteral("pirate") + ); } } // namespace diff --git a/killwindow.h b/killwindow.h index 801e11df5e..9c00b32ab8 100644 --- a/killwindow.h +++ b/killwindow.h @@ -23,14 +23,10 @@ along with this program. If not, see . #ifndef KWIN_KILLWINDOW_H #define KWIN_KILLWINDOW_H -#include "x11eventfilter.h" - -#include - namespace KWin { -class KillWindow : public X11EventFilter +class KillWindow { public: @@ -38,21 +34,6 @@ public: ~KillWindow(); void start(); - bool isActive() const { - return m_active; - } - void processEvent(xcb_generic_event_t *event); - - bool event(xcb_generic_event_t *event) override; - -private: - xcb_cursor_t createCursor(); - void release(); - void performKill(); - void handleKeyPress(xcb_keycode_t keycode, uint16_t state); - void handleButtonRelease(xcb_button_t button, xcb_window_t window); - void killWindowId(xcb_window_t window_to_kill); - bool m_active; }; } // namespace diff --git a/platform.cpp b/platform.cpp index 1012440ab4..48bdd0f56c 100644 --- a/platform.cpp +++ b/platform.cpp @@ -363,4 +363,11 @@ void Platform::createOpenGLSafePoint(OpenGLSafePoint safePoint) Q_UNUSED(safePoint) } +void Platform::startInteractiveWindowSelection(std::function callback, const QByteArray &cursorName) +{ + // TODO: provide a basic implementation for Wayland platforms + Q_UNUSED(cursorName) + callback(nullptr); +} + } diff --git a/platform.h b/platform.h index 7badf837c2..48db560aed 100644 --- a/platform.h +++ b/platform.h @@ -26,6 +26,8 @@ along with this program. If not, see . #include #include +#include + namespace KWayland { namespace Server { class OutputConfigurationInterface; @@ -40,6 +42,7 @@ class OpenGLBackend; class QPainterBackend; class Screens; class ScreenEdges; +class Toplevel; class WaylandCursorTheme; class KWIN_EXPORT Platform : public QObject @@ -158,6 +161,25 @@ public: **/ virtual void createOpenGLSafePoint(OpenGLSafePoint safePoint); + /** + * Starts an interactive window selection process. + * + * Once the user selected a window the @p callback is invoked with the selected Toplevel as + * argument. In case the user cancels the interactive window selection or selecting a window is currently + * not possible (e.g. screen locked) the @p callback is invoked with a @c nullptr argument. + * + * During the interactive window selection the cursor is turned into a crosshair cursor unless + * @p cursorName is provided. The argument @p cursorName is a QByteArray instead of Qt::CursorShape + * to support the "pirate" cursor for kill window which is not wrapped by Qt::CursorShape. + * + * The default implementation does not support selecting windows yet and only invokes the + * @p callback with @c nullptr. + * + * @param callback The function to invoke once the interactive window selection ends + * @param cursorName The optional name of the cursor shape to use, default is crosshair + **/ + virtual void startInteractiveWindowSelection(std::function callback, const QByteArray &cursorName = QByteArray()); + bool usesSoftwareCursor() const { return m_softWareCursor; } diff --git a/plugins/platforms/x11/standalone/CMakeLists.txt b/plugins/platforms/x11/standalone/CMakeLists.txt index 4b7a6c70ab..e7f8c56648 100644 --- a/plugins/platforms/x11/standalone/CMakeLists.txt +++ b/plugins/platforms/x11/standalone/CMakeLists.txt @@ -4,6 +4,7 @@ set(X11PLATFORM_SOURCES x11cursor.cpp x11_platform.cpp screens_xrandr.cpp + windowselector.cpp ) if(X11_Xinput_FOUND) diff --git a/plugins/platforms/x11/standalone/windowselector.cpp b/plugins/platforms/x11/standalone/windowselector.cpp new file mode 100644 index 0000000000..a5fe3ed2cf --- /dev/null +++ b/plugins/platforms/x11/standalone/windowselector.cpp @@ -0,0 +1,231 @@ +/******************************************************************** + 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) 2012 Martin Gräßlin + +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 . +*********************************************************************/ +#include "windowselector.h" +#include "client.h" +#include "cursor.h" +#include "unmanaged.h" +#include "workspace.h" +#include "xcbutils.h" +// XLib +#include +#include +#include +// XCB +#include + +namespace KWin +{ + +WindowSelector::WindowSelector() + : X11EventFilter(QVector{XCB_BUTTON_PRESS, + XCB_BUTTON_RELEASE, + XCB_MOTION_NOTIFY, + XCB_ENTER_NOTIFY, + XCB_LEAVE_NOTIFY, + XCB_KEY_PRESS, + XCB_KEY_RELEASE, + XCB_FOCUS_IN, + XCB_FOCUS_OUT + }) + , m_active(false) +{ +} + +WindowSelector::~WindowSelector() +{ +} + +void WindowSelector::start(std::function callback, const QByteArray &cursorName) +{ + xcb_cursor_t cursor = createCursor(cursorName); + if (m_active) { + callback(nullptr); + return; + } + + xcb_connection_t *c = connection(); + ScopedCPointer grabPointer(xcb_grab_pointer_reply(c, xcb_grab_pointer_unchecked(c, false, rootWindow(), + XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE | + XCB_EVENT_MASK_POINTER_MOTION | + XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW, + XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, XCB_WINDOW_NONE, + cursor, XCB_TIME_CURRENT_TIME), NULL)); + if (grabPointer.isNull() || grabPointer->status != XCB_GRAB_STATUS_SUCCESS) { + callback(nullptr); + return; + } + m_active = grabXKeyboard(); + if (!m_active) { + xcb_ungrab_pointer(connection(), XCB_TIME_CURRENT_TIME); + callback(nullptr); + return; + } + grabXServer(); + m_callback = callback; +} + +xcb_cursor_t WindowSelector::createCursor(const QByteArray &cursorName) +{ + if (cursorName.isEmpty()) { + return Cursor::x11Cursor(Qt::CrossCursor); + } + xcb_cursor_t cursor = Cursor::x11Cursor(cursorName); + if (cursor != XCB_CURSOR_NONE) { + return cursor; + } + if (cursorName == QByteArrayLiteral("pirate")) { + // special handling for font pirate cursor + static xcb_cursor_t kill_cursor = XCB_CURSOR_NONE; + if (kill_cursor != XCB_CURSOR_NONE) { + return kill_cursor; + } + // fallback on font + xcb_connection_t *c = connection(); + const xcb_font_t cursorFont = xcb_generate_id(c); + xcb_open_font(c, cursorFont, strlen ("cursor"), "cursor"); + cursor = xcb_generate_id(c); + xcb_create_glyph_cursor(c, cursor, cursorFont, cursorFont, + XC_pirate, /* source character glyph */ + XC_pirate + 1, /* mask character glyph */ + 0, 0, 0, 0, 0, 0); /* r b g r b g */ + kill_cursor = cursor; + } + return cursor; +} + +void WindowSelector::processEvent(xcb_generic_event_t *event) +{ + if (event->response_type == XCB_BUTTON_RELEASE) { + xcb_button_release_event_t *buttonEvent = reinterpret_cast(event); + handleButtonRelease(buttonEvent->detail, buttonEvent->child); + } else if (event->response_type == XCB_KEY_PRESS) { + xcb_key_press_event_t *keyEvent = reinterpret_cast(event); + handleKeyPress(keyEvent->detail, keyEvent->state); + } +} + +bool WindowSelector::event(xcb_generic_event_t *event) +{ + if (!m_active) { + return false; + } + processEvent(event); + + return true; +} + +void WindowSelector::handleButtonRelease(xcb_button_t button, xcb_window_t window) +{ + if (button == XCB_BUTTON_INDEX_3) { + m_callback(nullptr); + release(); + return; + } + if (button == XCB_BUTTON_INDEX_1 || button == XCB_BUTTON_INDEX_2) { + selectWindowId(window); + release(); + return; + } +} + +void WindowSelector::handleKeyPress(xcb_keycode_t keycode, uint16_t state) +{ + xcb_key_symbols_t *symbols = xcb_key_symbols_alloc(connection()); + xcb_keysym_t kc = xcb_key_symbols_get_keysym(symbols, keycode, 0); + int mx = 0; + int my = 0; + const bool returnPressed = (kc == XK_Return) || (kc == XK_space); + const bool escapePressed = (kc == XK_Escape); + if (kc == XK_Left) { + mx = -10; + } + if (kc == XK_Right) { + mx = 10; + } + if (kc == XK_Up) { + my = -10; + } + if (kc == XK_Down) { + my = 10; + } + if (state & XCB_MOD_MASK_CONTROL) { + mx /= 10; + my /= 10; + } + Cursor::setPos(Cursor::pos() + QPoint(mx, my)); + if (returnPressed) { + selectWindowUnderPointer(); + } + if (returnPressed || escapePressed) { + if (escapePressed) { + m_callback(nullptr); + } + release(); + } + xcb_key_symbols_free(symbols); +} + +void WindowSelector::selectWindowUnderPointer() +{ + Xcb::Pointer pointer(rootWindow()); + if (!pointer.isNull() && pointer->child != XCB_WINDOW_NONE) { + selectWindowId(pointer->child); + } +} + +void WindowSelector::release() +{ + ungrabXKeyboard(); + xcb_ungrab_pointer(connection(), XCB_TIME_CURRENT_TIME); + ungrabXServer(); + m_active = false; + m_callback = std::function(); +} + +void WindowSelector::selectWindowId(xcb_window_t window_to_select) +{ + if (window_to_select == XCB_WINDOW_NONE) { + m_callback(nullptr); + return; + } + xcb_window_t window = window_to_select; + Client* client = NULL; + while (true) { + client = Workspace::self()->findClient(Predicate::FrameIdMatch, window); + if (client) { + break; // Found the client + } + Xcb::Tree tree(window); + if (window == tree->root) { + // We didn't find the client, probably an override-redirect window + break; + } + window = tree->parent; // Go up + } + if (client) { + m_callback(client); + } else { + m_callback(Workspace::self()->findUnmanaged(window)); + } +} + +} // namespace diff --git a/plugins/platforms/x11/standalone/windowselector.h b/plugins/platforms/x11/standalone/windowselector.h new file mode 100644 index 0000000000..3cbbbd22fc --- /dev/null +++ b/plugins/platforms/x11/standalone/windowselector.h @@ -0,0 +1,64 @@ +/******************************************************************** + 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) 2012 Martin Gräßlin + +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_KILLWINDOW_H +#define KWIN_KILLWINDOW_H + +#include "x11eventfilter.h" + +#include + +#include + +namespace KWin +{ +class Toplevel; + +class WindowSelector : public X11EventFilter +{ +public: + + WindowSelector(); + ~WindowSelector(); + + void start(std::function callback, const QByteArray &cursorName); + bool isActive() const { + return m_active; + } + void processEvent(xcb_generic_event_t *event); + + bool event(xcb_generic_event_t *event) override; + +private: + xcb_cursor_t createCursor(const QByteArray &cursorName); + void release(); + void selectWindowUnderPointer(); + void handleKeyPress(xcb_keycode_t keycode, uint16_t state); + void handleButtonRelease(xcb_button_t button, xcb_window_t window); + void selectWindowId(xcb_window_t window_to_kill); + bool m_active; + std::function m_callback; +}; + +} // namespace + +#endif diff --git a/plugins/platforms/x11/standalone/x11_platform.cpp b/plugins/platforms/x11/standalone/x11_platform.cpp index 73db0921f7..36f1a18669 100644 --- a/plugins/platforms/x11/standalone/x11_platform.cpp +++ b/plugins/platforms/x11/standalone/x11_platform.cpp @@ -20,6 +20,7 @@ along with this program. If not, see . #include "x11_platform.h" #include "x11cursor.h" #include "edge.h" +#include "windowselector.h" #include #include #if HAVE_EPOXY_GLX @@ -273,4 +274,12 @@ void X11StandalonePlatform::doShowCursor() xcb_xfixes_show_cursor(kwinApp()->x11Connection(), kwinApp()->x11RootWindow()); } +void X11StandalonePlatform::startInteractiveWindowSelection(std::function callback, const QByteArray &cursorName) +{ + if (m_windowSelector.isNull()) { + m_windowSelector.reset(new WindowSelector); + } + m_windowSelector->start(callback, cursorName); +} + } diff --git a/plugins/platforms/x11/standalone/x11_platform.h b/plugins/platforms/x11/standalone/x11_platform.h index e4811f1ae1..719c94a981 100644 --- a/plugins/platforms/x11/standalone/x11_platform.h +++ b/plugins/platforms/x11/standalone/x11_platform.h @@ -28,6 +28,7 @@ along with this program. If not, see . namespace KWin { class XInputIntegration; +class WindowSelector; class KWIN_EXPORT X11StandalonePlatform : public Platform { @@ -48,6 +49,7 @@ public: QString compositingNotPossibleReason() const override; bool openGLCompositingIsBroken() const override; void createOpenGLSafePoint(OpenGLSafePoint safePoint) override; + void startInteractiveWindowSelection(std::function callback, const QByteArray &cursorName = QByteArray()) override; PlatformCursorImage cursorImage() const override; @@ -71,6 +73,7 @@ private: QThread *m_openGLFreezeProtectionThread = nullptr; QTimer *m_openGLFreezeProtection = nullptr; Display *m_x11Display; + QScopedPointer m_windowSelector; }; diff --git a/utils.h b/utils.h index e9fa912328..0650996a8f 100644 --- a/utils.h +++ b/utils.h @@ -144,11 +144,11 @@ MaximizeMode operator^(MaximizeMode m1, MaximizeMode m2) template using ScopedCPointer = QScopedPointer; void updateXTime(); -void grabXServer(); -void ungrabXServer(); +void KWIN_EXPORT grabXServer(); +void KWIN_EXPORT ungrabXServer(); bool grabbedXServer(); -bool grabXKeyboard(xcb_window_t w = rootWindow()); -void ungrabXKeyboard(); +bool KWIN_EXPORT grabXKeyboard(xcb_window_t w = rootWindow()); +void KWIN_EXPORT ungrabXKeyboard(); /** * Small helper class which performs @link grabXServer in the ctor and