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 | |
8 | This program is free software; you can redistribute it and/or modify |
9 | it under the terms of the GNU General Public License as published by |
10 | the Free Software Foundation; either version 2 of the License, or |
11 | (at your option) any later version. |
12 | |
13 | This program is distributed in the hope that it will be useful, |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | GNU General Public License for more details. |
17 | |
18 | You should have received a copy of the GNU General Public License |
19 | along with this program. If not, see <http://www.gnu.org/licenses/>. |
20 | *********************************************************************/ |
21 | |
22 | /* |
23 | |
24 | This file is for (very) small utility functions/classes. |
25 | |
26 | */ |
27 | |
28 | #include "utils.h" |
29 | |
30 | #include <kxerrorhandler.h> |
31 | #include <X11/Xatom.h> |
32 | |
33 | #ifndef KCMRULES |
34 | #include <assert.h> |
35 | #include <kdebug.h> |
36 | #include <kkeyserver.h> |
37 | |
38 | #include <X11/Xlib.h> |
39 | #include <X11/extensions/shape.h> |
40 | #include <QX11Info> |
41 | |
42 | #include <stdio.h> |
43 | |
44 | #include "atoms.h" |
45 | #include "cursor.h" |
46 | #include "workspace.h" |
47 | |
48 | #endif |
49 | |
50 | namespace KWin |
51 | { |
52 | |
53 | #ifndef KCMRULES |
54 | |
55 | //************************************ |
56 | // StrutRect |
57 | //************************************ |
58 | |
59 | StrutRect::StrutRect(QRect rect, StrutArea area) |
60 | : QRect(rect) |
61 | , m_area(area) |
62 | { |
63 | } |
64 | |
65 | StrutRect::StrutRect(const StrutRect& other) |
66 | : QRect(other) |
67 | , m_area(other.area()) |
68 | { |
69 | } |
70 | |
71 | //************************************ |
72 | // Motif |
73 | //************************************ |
74 | |
75 | void Motif::readFlags(xcb_window_t w, bool& got_noborder, bool& noborder, |
76 | bool& resize, bool& move, bool& minimize, bool& maximize, bool& close) |
77 | { |
78 | Atom type; |
79 | int format; |
80 | unsigned long length, after; |
81 | unsigned char* data; |
82 | MwmHints* hints = 0; |
83 | if (XGetWindowProperty(display(), w, atoms->motif_wm_hints, 0, 5, |
84 | false, atoms->motif_wm_hints, &type, &format, |
85 | &length, &after, &data) == Success) { |
86 | if (data) |
87 | hints = (MwmHints*) data; |
88 | } |
89 | got_noborder = false; |
90 | noborder = false; |
91 | resize = true; |
92 | move = true; |
93 | minimize = true; |
94 | maximize = true; |
95 | close = true; |
96 | if (hints) { |
97 | // To quote from Metacity 'We support those MWM hints deemed non-stupid' |
98 | if (hints->flags & MWM_HINTS_FUNCTIONS) { |
99 | // if MWM_FUNC_ALL is set, other flags say what to turn _off_ |
100 | bool set_value = ((hints->functions & MWM_FUNC_ALL) == 0); |
101 | resize = move = minimize = maximize = close = !set_value; |
102 | if (hints->functions & MWM_FUNC_RESIZE) |
103 | resize = set_value; |
104 | if (hints->functions & MWM_FUNC_MOVE) |
105 | move = set_value; |
106 | if (hints->functions & MWM_FUNC_MINIMIZE) |
107 | minimize = set_value; |
108 | if (hints->functions & MWM_FUNC_MAXIMIZE) |
109 | maximize = set_value; |
110 | if (hints->functions & MWM_FUNC_CLOSE) |
111 | close = set_value; |
112 | } |
113 | if (hints->flags & MWM_HINTS_DECORATIONS) { |
114 | got_noborder = true; |
115 | noborder = !hints->decorations; |
116 | } |
117 | XFree(data); |
118 | } |
119 | } |
120 | |
121 | #endif |
122 | |
123 | QByteArray getStringProperty(xcb_window_t w, xcb_atom_t prop, char separator) |
124 | { |
125 | const xcb_get_property_cookie_t c = xcb_get_property_unchecked(connection(), false, w, prop, |
126 | XCB_ATOM_STRING, 0, 10000); |
127 | ScopedCPointer<xcb_get_property_reply_t> property(xcb_get_property_reply(connection(), c, NULL)); |
128 | if (property.isNull() || property->type == XCB_ATOM_NONE) { |
129 | return QByteArray(); |
130 | } |
131 | char *data = static_cast<char*>(xcb_get_property_value(property.data())); |
132 | int length = property->value_len; |
133 | if (data && separator) { |
134 | for (uint32_t i = 0; i < property->value_len; ++i) { |
135 | if (!data[i] && i + 1 < property->value_len) { |
136 | data[i] = separator; |
137 | } else { |
138 | length = i; |
139 | } |
140 | } |
141 | } |
142 | return QByteArray(data, length); |
143 | } |
144 | |
145 | #ifndef KCMRULES |
146 | static Time next_x_time; |
147 | static Bool update_x_time_predicate(Display*, XEvent* event, XPointer) |
148 | { |
149 | if (next_x_time != CurrentTime) |
150 | return False; |
151 | // from qapplication_x11.cpp |
152 | switch(event->type) { |
153 | case ButtonPress: |
154 | // fallthrough intended |
155 | case ButtonRelease: |
156 | next_x_time = event->xbutton.time; |
157 | break; |
158 | case MotionNotify: |
159 | next_x_time = event->xmotion.time; |
160 | break; |
161 | case KeyPress: |
162 | // fallthrough intended |
163 | case KeyRelease: |
164 | next_x_time = event->xkey.time; |
165 | break; |
166 | case PropertyNotify: |
167 | next_x_time = event->xproperty.time; |
168 | break; |
169 | case EnterNotify: |
170 | case LeaveNotify: |
171 | next_x_time = event->xcrossing.time; |
172 | break; |
173 | case SelectionClear: |
174 | next_x_time = event->xselectionclear.time; |
175 | break; |
176 | default: |
177 | break; |
178 | } |
179 | return False; |
180 | } |
181 | |
182 | /* |
183 | Updates xTime(). This used to simply fetch current timestamp from the server, |
184 | but that can cause xTime() to be newer than timestamp of events that are |
185 | still in our events queue, thus e.g. making XSetInputFocus() caused by such |
186 | event to be ignored. Therefore events queue is searched for first |
187 | event with timestamp, and extra PropertyNotify is generated in order to make |
188 | sure such event is found. |
189 | */ |
190 | void updateXTime() |
191 | { |
192 | static QWidget* w = 0; |
193 | if (!w) |
194 | w = new QWidget; |
195 | long data = 1; |
196 | XChangeProperty(display(), w->winId(), atoms->kwin_running, atoms->kwin_running, 32, |
197 | PropModeAppend, (unsigned char*) &data, 1); |
198 | next_x_time = CurrentTime; |
199 | XEvent dummy; |
200 | XCheckIfEvent(display(), &dummy, update_x_time_predicate, NULL); |
201 | if (next_x_time == CurrentTime) { |
202 | XSync(display(), False); |
203 | XCheckIfEvent(display(), &dummy, update_x_time_predicate, NULL); |
204 | } |
205 | assert(next_x_time != CurrentTime); |
206 | QX11Info::setAppTime(next_x_time); |
207 | XEvent ev; // remove the PropertyNotify event from the events queue |
208 | XWindowEvent(display(), w->winId(), PropertyChangeMask, &ev); |
209 | } |
210 | |
211 | static int server_grab_count = 0; |
212 | |
213 | void grabXServer() |
214 | { |
215 | if (++server_grab_count == 1) |
216 | xcb_grab_server(connection()); |
217 | } |
218 | |
219 | void ungrabXServer() |
220 | { |
221 | assert(server_grab_count > 0); |
222 | if (--server_grab_count == 0) { |
223 | xcb_ungrab_server(connection()); |
224 | xcb_flush(connection()); |
225 | } |
226 | } |
227 | |
228 | bool grabbedXServer() |
229 | { |
230 | return server_grab_count > 0; |
231 | } |
232 | |
233 | static bool keyboard_grabbed = false; |
234 | |
235 | bool grabXKeyboard(xcb_window_t w) |
236 | { |
237 | if (QWidget::keyboardGrabber() != NULL) |
238 | return false; |
239 | if (keyboard_grabbed) |
240 | return false; |
241 | if (qApp->activePopupWidget() != NULL) |
242 | return false; |
243 | if (w == XCB_WINDOW_NONE) |
244 | w = rootWindow(); |
245 | const xcb_grab_keyboard_cookie_t c = xcb_grab_keyboard_unchecked(connection(), false, w, xTime(), |
246 | XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC); |
247 | ScopedCPointer<xcb_grab_keyboard_reply_t> grab(xcb_grab_keyboard_reply(connection(), c, NULL)); |
248 | if (grab.isNull()) { |
249 | return false; |
250 | } |
251 | if (grab->status != XCB_GRAB_STATUS_SUCCESS) { |
252 | return false; |
253 | } |
254 | keyboard_grabbed = true; |
255 | return true; |
256 | } |
257 | |
258 | void ungrabXKeyboard() |
259 | { |
260 | if (!keyboard_grabbed) { |
261 | // grabXKeyboard() may fail sometimes, so don't fail, but at least warn anyway |
262 | kDebug(1212) << "ungrabXKeyboard() called but keyboard not grabbed!" ; |
263 | } |
264 | keyboard_grabbed = false; |
265 | xcb_ungrab_keyboard(connection(), XCB_TIME_CURRENT_TIME); |
266 | } |
267 | |
268 | QPoint cursorPos() |
269 | { |
270 | return Cursor::self()->pos(); |
271 | } |
272 | |
273 | // converting between X11 mouse/keyboard state mask and Qt button/keyboard states |
274 | |
275 | int qtToX11Button(Qt::MouseButton button) |
276 | { |
277 | if (button == Qt::LeftButton) |
278 | return XCB_BUTTON_INDEX_1; |
279 | else if (button == Qt::MidButton) |
280 | return XCB_BUTTON_INDEX_2; |
281 | else if (button == Qt::RightButton) |
282 | return XCB_BUTTON_INDEX_3; |
283 | return XCB_BUTTON_INDEX_ANY; // 0 |
284 | } |
285 | |
286 | Qt::MouseButton x11ToQtMouseButton(int button) |
287 | { |
288 | if (button == XCB_BUTTON_INDEX_1) |
289 | return Qt::LeftButton; |
290 | if (button == XCB_BUTTON_INDEX_2) |
291 | return Qt::MidButton; |
292 | if (button == XCB_BUTTON_INDEX_3) |
293 | return Qt::RightButton; |
294 | if (button == XCB_BUTTON_INDEX_4) |
295 | return Qt::XButton1; |
296 | if (button == XCB_BUTTON_INDEX_5) |
297 | return Qt::XButton2; |
298 | return Qt::NoButton; |
299 | } |
300 | |
301 | int qtToX11State(Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers) |
302 | { |
303 | int ret = 0; |
304 | if (buttons & Qt::LeftButton) |
305 | ret |= XCB_KEY_BUT_MASK_BUTTON_1; |
306 | if (buttons & Qt::MidButton) |
307 | ret |= XCB_KEY_BUT_MASK_BUTTON_2; |
308 | if (buttons & Qt::RightButton) |
309 | ret |= XCB_KEY_BUT_MASK_BUTTON_3; |
310 | if (modifiers & Qt::ShiftModifier) |
311 | ret |= XCB_KEY_BUT_MASK_SHIFT; |
312 | if (modifiers & Qt::ControlModifier) |
313 | ret |= XCB_KEY_BUT_MASK_CONTROL; |
314 | if (modifiers & Qt::AltModifier) |
315 | ret |= KKeyServer::modXAlt(); |
316 | if (modifiers & Qt::MetaModifier) |
317 | ret |= KKeyServer::modXMeta(); |
318 | return ret; |
319 | } |
320 | |
321 | Qt::MouseButtons x11ToQtMouseButtons(int state) |
322 | { |
323 | Qt::MouseButtons ret = 0; |
324 | if (state & XCB_KEY_BUT_MASK_BUTTON_1) |
325 | ret |= Qt::LeftButton; |
326 | if (state & XCB_KEY_BUT_MASK_BUTTON_2) |
327 | ret |= Qt::MidButton; |
328 | if (state & XCB_KEY_BUT_MASK_BUTTON_3) |
329 | ret |= Qt::RightButton; |
330 | if (state & XCB_KEY_BUT_MASK_BUTTON_4) |
331 | ret |= Qt::XButton1; |
332 | if (state & XCB_KEY_BUT_MASK_BUTTON_5) |
333 | ret |= Qt::XButton2; |
334 | return ret; |
335 | } |
336 | |
337 | Qt::KeyboardModifiers x11ToQtKeyboardModifiers(int state) |
338 | { |
339 | Qt::KeyboardModifiers ret = 0; |
340 | if (state & XCB_KEY_BUT_MASK_SHIFT) |
341 | ret |= Qt::ShiftModifier; |
342 | if (state & XCB_KEY_BUT_MASK_CONTROL) |
343 | ret |= Qt::ControlModifier; |
344 | if (state & KKeyServer::modXAlt()) |
345 | ret |= Qt::AltModifier; |
346 | if (state & KKeyServer::modXMeta()) |
347 | ret |= Qt::MetaModifier; |
348 | return ret; |
349 | } |
350 | |
351 | #endif |
352 | } // namespace |
353 | |
354 | #ifndef KCMRULES |
355 | #include "utils.moc" |
356 | #endif |
357 | |