1/********************************************************************
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org>
6Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org>
7
8This program is free software; you can redistribute it and/or modify
9it under the terms of the GNU General Public License as published by
10the Free Software Foundation; either version 2 of the License, or
11(at your option) any later version.
12
13This program is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU General Public License
19along 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
50namespace KWin
51{
52
53#ifndef KCMRULES
54
55//************************************
56// StrutRect
57//************************************
58
59StrutRect::StrutRect(QRect rect, StrutArea area)
60 : QRect(rect)
61 , m_area(area)
62{
63}
64
65StrutRect::StrutRect(const StrutRect& other)
66 : QRect(other)
67 , m_area(other.area())
68{
69}
70
71//************************************
72// Motif
73//************************************
74
75void 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
123QByteArray 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
146static Time next_x_time;
147static 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*/
190void 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
211static int server_grab_count = 0;
212
213void grabXServer()
214{
215 if (++server_grab_count == 1)
216 xcb_grab_server(connection());
217}
218
219void 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
228bool grabbedXServer()
229{
230 return server_grab_count > 0;
231}
232
233static bool keyboard_grabbed = false;
234
235bool 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
258void 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
268QPoint cursorPos()
269{
270 return Cursor::self()->pos();
271}
272
273// converting between X11 mouse/keyboard state mask and Qt button/keyboard states
274
275int 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
286Qt::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
301int 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
321Qt::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
337Qt::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