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