1 /*
2 * This file is part of Licq, an instant messaging client for UNIX.
3 * Copyright (C) 2006-2014 Licq developers <licq-dev@googlegroups.com>
4 *
5 * Licq is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * Licq is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with Licq; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20 #include "support.h"
21
22 #include "config.h"
23
24 #include <cassert>
25
26 #include <QCoreApplication>
27
28 #if defined(USE_KDE)
29 #include <KDE/KWindowSystem>
30 #endif
31
32 #if defined(Q_WS_X11)
33 #include <QX11Info>
34 #include <X11/X.h>
35 #include <X11/Xatom.h>
36 #include <X11/Xlib.h>
37 #include <X11/Xutil.h>
38 #include <X11/keysym.h>
39 #endif /* defined(Q_WS_X11) */
40
41 #include <licq/logging/log.h>
42
43 using namespace LicqQtGui;
44
changeWinSticky(WId win,bool stick)45 void Support::changeWinSticky(WId win, bool stick)
46 {
47 #if defined(USE_KDE) || defined(Q_WS_X11)
48 Licq::gLog.info("Setting Sticky state of window 0x%lx to %s",
49 static_cast<unsigned long>(win), stick ? "true" : "false");
50 #endif
51
52 #if defined(USE_KDE)
53 KWindowSystem::setOnAllDesktops(win, stick);
54 #elif defined(Q_WS_X11)
55
56 Display* dsp = QX11Info::display();
57 Window root = DefaultRootWindow(dsp);
58
59 unsigned long desktop = ~(0UL);
60
61 if (!stick)
62 {
63 unsigned char* tmp = getWindowProperty(root, "_NET_CURRENT_DESKTOP");
64
65 if (tmp == NULL)
66 Licq::gLog.info("Error reading current desktop property");
67 else
68 {
69 desktop = *(reinterpret_cast<unsigned long*>(tmp));
70 XFree(tmp);
71 }
72 }
73
74 XEvent xev;
75 xev.type = ClientMessage;
76 xev.xclient.type = ClientMessage;
77 xev.xclient.display = dsp;
78 xev.xclient.window = win;
79 xev.xclient.message_type = XInternAtom(dsp, "_NET_WM_DESKTOP", False);
80 xev.xclient.format = 32;
81 xev.xclient.data.l[0] = desktop;
82
83 XSendEvent(dsp, root, False,
84 SubstructureRedirectMask | SubstructureNotifyMask, &xev);
85
86 #endif /* defined(Q_WS_X11) */
87 }
88
setWidgetProps(QWidget * widget,const QString & name)89 void Support::setWidgetProps(QWidget* widget, const QString& name)
90 {
91 assert(widget != NULL && !name.isEmpty());
92 if (widget == NULL || name.isEmpty())
93 return;
94
95 widget->setObjectName(name);
96
97 #if defined(Q_WS_X11)
98 if (widget->isWindow())
99 {
100 Display* display = widget->x11Info().display();
101 WId win = widget->winId();
102
103 XClassHint classHint;
104 if (XGetClassHint(display, win, &classHint) == 0)
105 return;
106
107 XFree(classHint.res_name);
108 QByteArray local8Bit = name.toLocal8Bit();
109
110 classHint.res_name = local8Bit.data();
111 XSetClassHint(display, win, &classHint);
112
113 XFree(classHint.res_class);
114 }
115 #endif
116 }
117
ghostWindow(WId win)118 void Support::ghostWindow(WId win)
119 {
120 #if defined(Q_WS_X11)
121 Display* dsp = QX11Info::display();
122 Window root = DefaultRootWindow(dsp);
123
124 Atom win_state = XInternAtom(dsp, "_NET_WM_STATE", False);
125 Atom win_state_add = XInternAtom(dsp, "_NET_WM_STATE_ADD", False);
126 Atom win_state_settings[] =
127 {
128 XInternAtom(dsp, "_NET_WM_STATE_SKIP_TASKBAR", False),
129 XInternAtom(dsp, "_NET_WM_STATE_SKIP_PAGER", False)
130 };
131 XChangeProperty(dsp, win, win_state, XA_ATOM, 32, PropModeReplace,
132 reinterpret_cast<unsigned char*>(&win_state_settings), 2);
133
134 XEvent xev;
135 xev.type = ClientMessage;
136 xev.xclient.type = ClientMessage;
137 xev.xclient.display = dsp;
138 xev.xclient.window = win;
139 xev.xclient.message_type = win_state;
140 xev.xclient.format = 32;
141 xev.xclient.data.l[0] = win_state_add;
142 xev.xclient.data.l[1] = win_state_settings[0];
143 xev.xclient.data.l[2] = win_state_settings[1];
144
145 XSendEvent(dsp, root, false,
146 SubstructureRedirectMask | SubstructureNotifyMask, &xev);
147
148 #endif
149 }
150
dockWindow(WId win)151 WId Support::dockWindow(WId win)
152 {
153 WId handler = 0;
154 #if defined(Q_WS_X11)
155 // Create the new top-level window
156 Display* dsp = QX11Info::display();
157 Window root = XDefaultRootWindow(dsp);
158 Window newWin = XCreateSimpleWindow(dsp, root, 0, 0, 64, 64, 0, 0, 0);
159
160 // Reparent our widget to newly created window
161 XReparentWindow(dsp, win, newWin, 0, 0);
162
163 // Replicate WM_CLASS attribute
164 XClassHint classhint;
165 XGetClassHint(dsp, win, &classhint);
166 XSetClassHint(dsp, newWin, &classhint);
167
168 // Replicate command line arguments
169 QStringList args = QCoreApplication::arguments();
170 QVector<char*> argv;
171 while (!args.empty())
172 {
173 QString arg = args.takeFirst();
174 argv << arg.toLocal8Bit().data();
175 }
176 XSetCommand(dsp, newWin, argv.data(), argv.count());
177
178 // Make the new window be a group leader and
179 // point it to the window representing its icon
180 XWMHints* hints = XAllocWMHints();
181 hints->icon_window = win;
182 hints->window_group = newWin;
183 hints->initial_state = WithdrawnState;
184 hints->flags = IconWindowHint | WindowGroupHint | StateHint;
185 XSetWMHints(dsp, newWin, hints);
186 XFree(hints);
187
188 // And finally, show the window
189 XMapWindow(dsp, newWin);
190
191 handler = newWin;
192 #endif
193 return handler;
194 }
195
undockWindow(WId win,WId handler)196 void Support::undockWindow(WId win, WId handler)
197 {
198 if (handler == 0)
199 return;
200
201 #if defined(Q_WS_X11)
202 Display* dsp = QX11Info::display();
203 Window root = XDefaultRootWindow(dsp);
204
205 XUnmapWindow(dsp, win);
206 XUnmapWindow(dsp, handler);
207
208 // Reparent both windows to the root and hide them
209 XReparentWindow(dsp, win, root, -100, -100);
210 XReparentWindow(dsp, handler, root, -100, -100);
211
212 // And destroy the handler
213 XDestroyWindow(dsp, handler);
214 #endif
215 }
216
217 #if defined(Q_WS_X11)
keyToXMod(int keyCode)218 unsigned Support::keyToXMod(int keyCode)
219 {
220 unsigned mod = 0;
221
222 if (keyCode != 0)
223 {
224 if (keyCode & Qt::SHIFT)
225 mod |= ShiftMask;
226 if (keyCode & Qt::CTRL)
227 mod |= ControlMask;
228 if (keyCode & Qt::ALT)
229 mod |= Mod1Mask;
230 if (keyCode & Qt::META)
231 mod |= Mod4Mask;
232 }
233
234 return mod;
235 }
236
keyToXSym(int keyCode)237 unsigned Support::keyToXSym(int keyCode)
238 {
239 unsigned keysym = 0;
240
241 char* toks[4];
242 char* next_tok;
243 char sKey[100];
244 int nb_toks = 0;
245 QString s = QKeySequence(keyCode);
246
247 if (s.isEmpty())
248 return keysym;
249
250 qstrncpy(sKey, s.toLatin1(), sizeof(sKey));
251 next_tok = strtok(sKey, "+");
252
253 if (next_tok == 0L)
254 return 0;
255
256 do
257 {
258 toks[nb_toks] = next_tok;
259 nb_toks++;
260 if (nb_toks >= 4)
261 return 0;
262 next_tok = strtok(0L, "+");
263 } while (next_tok != 0L);
264
265 // Test for exactly one key (other tokens are accelerators)
266 // Fill the keycode with infos
267 bool keyFound = false;
268
269 for (int i = 0; i < nb_toks; i++)
270 {
271 if (qstricmp(toks[i], "SHIFT") != 0 &&
272 qstricmp(toks[i], "CTRL") != 0 &&
273 qstricmp(toks[i], "ALT") != 0 &&
274 qstricmp(toks[i], "META") != 0)
275 {
276 if (keyFound)
277 return 0;
278 keyFound = true;
279 QString l = toks[i];
280 l = l.toLower();
281 keysym = XStringToKeysym(l.toLatin1());
282 if (keysym == NoSymbol)
283 keysym = XStringToKeysym(toks[i]);
284 if (keysym == NoSymbol)
285 return 0;
286 }
287 }
288
289 return keysym;
290 }
291
grabKey(Display * dsp,Qt::HANDLE rootWin,int key,bool enable)292 void Support::grabKey(Display* dsp, Qt::HANDLE rootWin, int key, bool enable)
293 {
294 KeyCode keycode = XKeysymToKeycode(dsp, keyToXSym(key));
295 unsigned basemod = keyToXMod(key);
296
297 // No extra modifiers
298 XGrabKey(dsp, keycode, basemod, rootWin, enable, GrabModeAsync, GrabModeSync);
299
300 // Caps Lock
301 XGrabKey(dsp, keycode, basemod | LockMask, rootWin, enable, GrabModeAsync, GrabModeSync);
302
303 // Num Lock
304 XGrabKey(dsp, keycode, basemod | Mod2Mask, rootWin, enable, GrabModeAsync, GrabModeSync);
305
306 // Caps Lock & Num Lock
307 XGrabKey(dsp, keycode, basemod | LockMask | Mod2Mask, rootWin, enable, GrabModeAsync, GrabModeSync);
308 }
309
getWindowProperty(WId win,const char * prop)310 unsigned char* Support::getWindowProperty(WId win, const char* prop)
311 {
312 Display* dsp = QX11Info::display();
313
314 // We inhibit new Atom creation since if you request for it
315 // then such Atom most probably exists already.
316 // Otherwise, no surprise we return NULL here.
317 Atom reqAtom = XInternAtom(dsp, prop, True);
318
319 if (reqAtom == None)
320 return NULL;
321
322 int retCheck = None;
323 Atom retType = None;
324 int retFormat = 0;
325 unsigned long retItems = 0UL;
326 unsigned long retMoreBytes = 0UL;
327 unsigned char* retValue = NULL;
328
329 // Check if the property exists and calculate its length.
330 retCheck = XGetWindowProperty(dsp, win,
331 reqAtom, 0L, 0L, False, AnyPropertyType,
332 &retType, &retFormat, &retItems, &retMoreBytes, &retValue);
333
334 // The value is most probably empty, since we requested to read
335 // only 0L length, thus, it's just useless...
336 if (retValue != NULL)
337 {
338 XFree(retValue);
339 retValue = NULL;
340 }
341
342 if (retCheck != Success ||
343 retType == None ||
344 retMoreBytes == 0)
345 return NULL;
346
347 // These are not needed for now.
348 retCheck = None;
349 retFormat = 0;
350 retItems = 0UL;
351
352 // Convert the byte length into 32bit multiples.
353 if (retMoreBytes % 4 != 0)
354 retMoreBytes += 4 - retMoreBytes % 4;
355 retMoreBytes /= 4;
356
357 // Now request the actual property value with correct length and type.
358 retCheck = XGetWindowProperty(dsp, win,
359 reqAtom, 0L, retMoreBytes, False, retType,
360 &retType, &retFormat, &retItems, &retMoreBytes, &retValue);
361
362 if (retCheck != Success ||
363 retMoreBytes != 0)
364 {
365 if (retValue != NULL)
366 XFree(retValue);
367 return NULL;
368 }
369
370 return retValue;
371 }
372 #endif /* defined(Q_WS_X11) */
373