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