1 | /******************************************************************** |
2 | KWin - the KDE window manager |
3 | This file is part of the KDE project. |
4 | |
5 | Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org> |
6 | Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> |
7 | Copyright (C) 2012 Martin Gräßlin <mgraesslin@kde.org> |
8 | |
9 | This program is free software; you can redistribute it and/or modify |
10 | it under the terms of the GNU General Public License as published by |
11 | the Free Software Foundation; either version 2 of the License, or |
12 | (at your option) any later version. |
13 | |
14 | This program is distributed in the hope that it will be useful, |
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | GNU General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU General Public License |
20 | along with this program. If not, see <http://www.gnu.org/licenses/>. |
21 | *********************************************************************/ |
22 | #include "killwindow.h" |
23 | #include "client.h" |
24 | #include "cursor.h" |
25 | #include "workspace.h" |
26 | // Qt |
27 | #include <QCursor> |
28 | // XLib |
29 | #include <X11/cursorfont.h> |
30 | #include <X11/Xcursor/Xcursor.h> |
31 | // XCB |
32 | #include <xcb/xcb_keysyms.h> |
33 | |
34 | namespace KWin |
35 | { |
36 | |
37 | KillWindow::KillWindow() |
38 | : m_active(false) |
39 | { |
40 | } |
41 | |
42 | KillWindow::~KillWindow() |
43 | { |
44 | } |
45 | |
46 | void KillWindow::start() |
47 | { |
48 | static xcb_cursor_t kill_cursor = XCB_CURSOR_NONE; |
49 | if (kill_cursor == XCB_CURSOR_NONE) { |
50 | kill_cursor = createCursor(); |
51 | } |
52 | if (m_active) { |
53 | return; |
54 | } |
55 | |
56 | xcb_connection_t *c = connection(); |
57 | ScopedCPointer<xcb_grab_pointer_reply_t> grabPointer(xcb_grab_pointer_reply(c, xcb_grab_pointer_unchecked(c, false, rootWindow(), |
58 | XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE | |
59 | XCB_EVENT_MASK_POINTER_MOTION | |
60 | XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW, |
61 | XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, XCB_WINDOW_NONE, |
62 | kill_cursor, XCB_TIME_CURRENT_TIME), NULL)); |
63 | if (grabPointer.isNull() || grabPointer->status != XCB_GRAB_STATUS_SUCCESS) { |
64 | return; |
65 | } |
66 | m_active = grabXKeyboard(); |
67 | if (!m_active) { |
68 | xcb_ungrab_pointer(connection(), XCB_TIME_CURRENT_TIME); |
69 | return; |
70 | } |
71 | grabXServer(); |
72 | } |
73 | |
74 | xcb_cursor_t KillWindow::createCursor() |
75 | { |
76 | // XCursor is an XLib only lib |
77 | const char *theme = XcursorGetTheme(display()); |
78 | const int size = XcursorGetDefaultSize(display()); |
79 | XcursorImage *ximg = XcursorLibraryLoadImage("pirate" , theme, size); |
80 | if (ximg) { |
81 | xcb_cursor_t cursor = XcursorImageLoadCursor(display(), ximg); |
82 | XcursorImageDestroy(ximg); |
83 | return cursor; |
84 | } |
85 | // fallback on font |
86 | xcb_connection_t *c = connection(); |
87 | const xcb_font_t cursorFont = xcb_generate_id(c); |
88 | xcb_open_font(c, cursorFont, strlen ("cursor" ), "cursor" ); |
89 | xcb_cursor_t cursor = xcb_generate_id(c); |
90 | xcb_create_glyph_cursor(c, cursor, cursorFont, cursorFont, |
91 | XC_pirate, /* source character glyph */ |
92 | XC_pirate + 1, /* mask character glyph */ |
93 | 0, 0, 0, 0, 0, 0); /* r b g r b g */ |
94 | return cursor; |
95 | } |
96 | |
97 | bool KillWindow::isResponsibleForEvent(int eventType) const |
98 | { |
99 | switch (eventType) { |
100 | case XCB_BUTTON_PRESS: |
101 | case XCB_BUTTON_RELEASE: |
102 | case XCB_MOTION_NOTIFY: |
103 | case XCB_ENTER_NOTIFY: |
104 | case XCB_LEAVE_NOTIFY: |
105 | case XCB_KEY_PRESS: |
106 | case XCB_KEY_RELEASE: |
107 | case XCB_FOCUS_IN: |
108 | case XCB_FOCUS_OUT: |
109 | return true; |
110 | default: |
111 | return false; |
112 | } |
113 | } |
114 | |
115 | void KillWindow::processEvent(XEvent *event) |
116 | { |
117 | if (event->type == XCB_BUTTON_RELEASE) { |
118 | handleButtonRelease(event->xbutton.button, event->xbutton.subwindow); |
119 | } else if (event->type == XCB_KEY_PRESS) { |
120 | handleKeyPress(event->xkey.keycode, event->xkey.state); |
121 | } |
122 | } |
123 | |
124 | void KillWindow::processEvent(xcb_generic_event_t *event) |
125 | { |
126 | if (event->response_type == XCB_BUTTON_RELEASE) { |
127 | xcb_button_release_event_t *buttonEvent = reinterpret_cast<xcb_button_release_event_t*>(event); |
128 | handleButtonRelease(buttonEvent->detail, buttonEvent->child); |
129 | } else if (event->response_type == XCB_KEY_PRESS) { |
130 | xcb_key_press_event_t *keyEvent = reinterpret_cast<xcb_key_press_event_t*>(event); |
131 | handleKeyPress(keyEvent->detail, keyEvent->state); |
132 | } |
133 | } |
134 | |
135 | void KillWindow::handleButtonRelease(xcb_button_t button, xcb_window_t window) |
136 | { |
137 | if (button == XCB_BUTTON_INDEX_3) { |
138 | release(); |
139 | return; |
140 | } |
141 | if (button == XCB_BUTTON_INDEX_1 || button == XCB_BUTTON_INDEX_2) { |
142 | killWindowId(window); |
143 | release(); |
144 | return; |
145 | } |
146 | } |
147 | |
148 | void KillWindow::handleKeyPress(xcb_keycode_t keycode, uint16_t state) |
149 | { |
150 | xcb_key_symbols_t *symbols = xcb_key_symbols_alloc(connection()); |
151 | xcb_keysym_t kc = xcb_key_symbols_get_keysym(symbols, keycode, 0); |
152 | int mx = 0; |
153 | int my = 0; |
154 | const bool returnPressed = (kc == XK_Return) || (kc == XK_space); |
155 | const bool escapePressed = (kc == XK_Escape); |
156 | if (kc == XK_Left) { |
157 | mx = -10; |
158 | } |
159 | if (kc == XK_Right) { |
160 | mx = 10; |
161 | } |
162 | if (kc == XK_Up) { |
163 | my = -10; |
164 | } |
165 | if (kc == XK_Down) { |
166 | my = 10; |
167 | } |
168 | if (state & XCB_MOD_MASK_CONTROL) { |
169 | mx /= 10; |
170 | my /= 10; |
171 | } |
172 | Cursor::setPos(Cursor::pos() + QPoint(mx, my)); |
173 | if (returnPressed) { |
174 | performKill(); |
175 | } |
176 | if (returnPressed || escapePressed) { |
177 | release(); |
178 | } |
179 | xcb_key_symbols_free(symbols); |
180 | } |
181 | |
182 | void KillWindow::performKill() |
183 | { |
184 | xcb_connection_t *c = connection(); |
185 | ScopedCPointer<xcb_query_pointer_reply_t> pointer(xcb_query_pointer_reply(c, xcb_query_pointer_unchecked(c, rootWindow()), NULL)); |
186 | if (!pointer.isNull() && pointer->child != XCB_WINDOW_NONE) { |
187 | killWindowId(pointer->child); |
188 | } |
189 | } |
190 | |
191 | void KillWindow::release() |
192 | { |
193 | ungrabXKeyboard(); |
194 | xcb_ungrab_pointer(connection(), XCB_TIME_CURRENT_TIME); |
195 | ungrabXServer(); |
196 | m_active = false; |
197 | } |
198 | |
199 | void KillWindow::killWindowId(xcb_window_t window_to_kill) |
200 | { |
201 | if (window_to_kill == XCB_WINDOW_NONE) |
202 | return; |
203 | xcb_window_t window = window_to_kill; |
204 | Client* client = NULL; |
205 | while (true) { |
206 | client = Workspace::self()->findClient(FrameIdMatchPredicate(window)); |
207 | if (client) { |
208 | break; // Found the client |
209 | } |
210 | Xcb::Tree tree(window); |
211 | if (window == tree->root) { |
212 | // We didn't find the client, probably an override-redirect window |
213 | break; |
214 | } |
215 | window = tree->parent; // Go up |
216 | } |
217 | if (client) |
218 | client->killWindow(); |
219 | else |
220 | xcb_kill_client(connection(), window_to_kill); |
221 | } |
222 | |
223 | } // namespace |
224 | |