]> git.saurik.com Git - wxWidgets.git/blame - src/unix/utilsx11.cpp
added support for gcc precompiled headers
[wxWidgets.git] / src / unix / utilsx11.cpp
CommitLineData
f618020a
MB
1/////////////////////////////////////////////////////////////////////////////
2// Name: src/unix/utilsx11.cpp
3// Purpose: Miscellaneous X11 functions
8166ab43 4// Author: Mattia Barbon, Vaclav Slavik, Robert Roebling
f618020a
MB
5// Modified by:
6// Created: 25.03.02
7// RCS-ID: $Id$
8// Copyright: (c) wxWindows team
6aa89a22 9// Licence: wxWindows licence
f618020a
MB
10/////////////////////////////////////////////////////////////////////////////
11
12#if defined(__WXX11__) || defined(__WXGTK__) || defined(__WXMOTIF__)
13
14#include "wx/unix/utilsx11.h"
15#include "wx/iconbndl.h"
16#include "wx/image.h"
17#include "wx/icon.h"
8166ab43 18#include "wx/log.h"
f618020a 19
130d96dc
JJ
20#ifdef __VMS
21#pragma message disable nosimpint
22#endif
f618020a
MB
23#include <X11/Xlib.h>
24#include <X11/Xatom.h>
130d96dc
JJ
25#ifdef __VMS
26#pragma message enable nosimpint
27#endif
f618020a 28
8166ab43
VS
29#ifdef __WXGTK20__
30#include <gdk/gdk.h>
31#include <gdk/gdkx.h>
32#endif
33
34// Various X11 Atoms used in this file:
35static Atom _NET_WM_ICON = 0;
36static Atom _NET_WM_STATE = 0;
37static Atom _NET_WM_STATE_FULLSCREEN = 0;
38static Atom _NET_WM_STATE_STAYS_ON_TOP = 0;
39static Atom _NET_WM_WINDOW_TYPE = 0;
40static Atom _NET_WM_WINDOW_TYPE_NORMAL = 0;
41static Atom _KDE_NET_WM_WINDOW_TYPE_OVERRIDE = 0;
42static Atom _WIN_LAYER = 0;
43static Atom KWIN_RUNNING = 0;
44#ifndef __WXGTK20__
45static Atom _NET_SUPPORTING_WM_CHECK = 0;
46static Atom _NET_SUPPORTED = 0;
47#endif
48
49#define wxMAKE_ATOM(name, display) \
50 if (name == 0) name = XInternAtom((display), #name, False)
51
52
53// Is the window mapped?
54static bool IsMapped(Display *display, Window window)
55{
56 XWindowAttributes attr;
57 XGetWindowAttributes(display, window, &attr);
58 return (attr.map_state != IsUnmapped);
59}
60
61
62
63// Suspends X11 errors. Used when we expect errors but they are not fatal
64// for us.
65class wxX11ErrorsSuspender
66{
67public:
68 wxX11ErrorsSuspender(Display *d) : m_display(d)
69 {
70 m_old = XSetErrorHandler(handler);
71 }
72 ~wxX11ErrorsSuspender()
73 {
74 XFlush(m_display);
75 XSetErrorHandler(m_old);
76 }
77private:
78 Display *m_display;
79 int (*m_old)(Display*, XErrorEvent *);
80 static int handler(Display *, XErrorEvent *) { return 0; }
81};
82
83
84
85// ----------------------------------------------------------------------------
86// Setting icons for window manager:
87// ----------------------------------------------------------------------------
88
f618020a
MB
89void wxSetIconsX11( WXDisplay* display, WXWindow window,
90 const wxIconBundle& ib )
91{
8601b2e1 92#if !wxUSE_NANOX
f618020a
MB
93 size_t size = 0;
94 size_t i, max = ib.m_icons.GetCount();
95
96 for( i = 0; i < max; ++i )
7efaed4d
MB
97 if( ib.m_icons[i].Ok() )
98 size += 2 + ib.m_icons[i].GetWidth() * ib.m_icons[i].GetHeight();
f618020a 99
8166ab43 100 wxMAKE_ATOM(_NET_WM_ICON, (Display*)display);
f618020a
MB
101
102 if( size > 0 )
103 {
104 wxUint32* data = new wxUint32[size];
105 wxUint32* ptr = data;
106
107 for( i = 0; i < max; ++i )
108 {
109 const wxImage image = ib.m_icons[i].ConvertToImage();
110 int width = image.GetWidth(), height = image.GetHeight();
111 unsigned char* imageData = image.GetData();
112 unsigned char* imageDataEnd = imageData + ( width * height * 3 );
113 bool hasMask = image.HasMask();
114 unsigned char rMask, gMask, bMask;
115 unsigned char r, g, b, a;
116
117 if( hasMask )
118 {
119 rMask = image.GetMaskRed();
120 gMask = image.GetMaskGreen();
121 bMask = image.GetMaskBlue();
122 }
0fc5dbf5
VZ
123 else // no mask, but still init the variables to avoid warnings
124 {
125 rMask =
126 gMask =
127 bMask = 0;
128 }
f618020a
MB
129
130 *ptr++ = width;
131 *ptr++ = height;
132
133 while( imageData < imageDataEnd ) {
134 r = imageData[0];
135 g = imageData[1];
136 b = imageData[2];
137 if( hasMask && r == rMask && g == gMask && b == bMask )
138 a = 0;
139 else
140 a = 255;
141
142 *ptr++ = ( a << 24 ) | ( r << 16 ) | ( g << 8 ) | b;
143
144 imageData += 3;
145 }
146 }
147
148 XChangeProperty( (Display*)display,
149 (Window)window,
8166ab43 150 _NET_WM_ICON,
f618020a
MB
151 XA_CARDINAL, 32,
152 PropModeReplace,
153 (unsigned char*)data, size );
154 delete[] data;
155 }
156 else
157 {
158 XDeleteProperty( (Display*)display,
159 (Window)window,
8166ab43 160 _NET_WM_ICON );
f618020a 161 }
8601b2e1 162#endif
f618020a
MB
163}
164
8166ab43
VS
165
166// ----------------------------------------------------------------------------
167// Fullscreen mode:
168// ----------------------------------------------------------------------------
169
170// NB: Setting fullscreen mode under X11 is a complicated matter. There was
171// no standard way of doing it until recently. ICCCM doesn't know the
172// concept of fullscreen windows and the only way to make a window
173// fullscreen is to remove decorations, resize it to cover entire screen
174// and set WIN_LAYER_ABOVE_DOCK.
175//
176// This doesn't always work, though. Specifically, at least kwin from
177// KDE 3 ignores the hint. The only way to make kwin accept our request
178// is to emulate the way Qt does it. That is, unmap the window, set
179// _NET_WM_WINDOW_TYPE to _KDE_NET_WM_WINDOW_TYPE_OVERRIDE (KDE extension),
180// add _NET_WM_STATE_STAYS_ON_TOP (ditto) to _NET_WM_STATE and map
181// the window again.
182//
183// Version 1.2 of Window Manager Specification (aka wm-spec aka
184// Extended Window Manager Hints) introduced _NET_WM_STATE_FULLSCREEN
185// window state which provides cleanest and simplest possible way of
186// making a window fullscreen. WM-spec is a de-facto standard adopted
187// by GNOME and KDE folks, but _NET_WM_STATE_FULLSCREEN isn't yet widely
188// supported. As of January 2003, only GNOME 2's default WM Metacity
189// implements, KDE will support it from version 3.2. At toolkits level,
190// GTK+ >= 2.1.2 uses it as the only method of making windows fullscreen
191// (that's why wxGTK will *not* switch to using gtk_window_fullscreen
192// unless it has better compatiblity with older WMs).
193//
194//
195// This is what wxWindows does in wxSetFullScreenStateX11:
196// 1) if _NET_WM_STATE_FULLSCREEN is supported, use it
197// 2) otherwise try WM-specific hacks (KDE, IceWM)
198// 3) use _WIN_LAYER and hope that the WM will recognize it
199// The code was tested with:
200// twm, IceWM, WindowMaker, Metacity, kwin, sawfish, lesstif-mwm
201
202
203#define WIN_LAYER_NORMAL 4
204#define WIN_LAYER_ABOVE_DOCK 10
205
206static void wxWinHintsSetLayer(Display *display, Window rootWnd,
207 Window window, int layer)
208{
209 wxX11ErrorsSuspender noerrors(display);
210
211 XEvent xev;
212
213 wxMAKE_ATOM( _WIN_LAYER, display );
214
215 if (IsMapped(display, window))
216 {
217 xev.type = ClientMessage;
218 xev.xclient.type = ClientMessage;
219 xev.xclient.window = window;
220 xev.xclient.message_type = _WIN_LAYER;
221 xev.xclient.format = 32;
222 xev.xclient.data.l[0] = (long)layer;
223 xev.xclient.data.l[1] = CurrentTime;
224
225 XSendEvent(display, rootWnd, False,
226 SubstructureNotifyMask, (XEvent*) &xev);
227 }
228 else
229 {
230 long data[1];
231
232 data[0] = layer;
233 XChangeProperty(display, window,
234 _WIN_LAYER, XA_CARDINAL, 32,
235 PropModeReplace, (unsigned char *)data, 1);
236 }
237}
238
239
240
241#ifdef __WXGTK20__
242static bool wxQueryWMspecSupport(Display* WXUNUSED(display),
243 Window WXUNUSED(rootWnd),
244 Atom (feature))
245{
246 GdkAtom gatom = gdk_x11_xatom_to_atom(feature);
247 return gdk_net_wm_supports(gatom);
248}
249#else
250static bool wxQueryWMspecSupport(Display *display, Window rootWnd, Atom feature)
251{
252 wxMAKE_ATOM(_NET_SUPPORTING_WM_CHECK, display);
253 wxMAKE_ATOM(_NET_SUPPORTED, display);
254
255 // FIXME: We may want to cache these checks. Note that we can't simply
256 // remember the results in global variable because the WM may go
257 // away and be replaced by another one! One possible approach
258 // would be invalidate the case every 15 seconds or so. Since this
259 // code is currently only used by wxTopLevelWindow::ShowFullScreen,
260 // it is not important that it is not optimized.
261 //
262 // If the WM supports ICCCM (i.e. the root window has
263 // _NET_SUPPORTING_WM_CHECK property that points to a WM-owned
264 // window), we could watch for DestroyNotify event on the window
265 // and invalidate our cache when the windows goes away (= WM
266 // is replaced by another one). This is what GTK+ 2 does.
267 // Let's do it only if it is needed, it requires changes to
268 // the event loop.
269
270 Atom type;
271 Window *wins;
272 Atom *atoms;
273 int format;
274 unsigned long after;
275 unsigned long nwins, natoms;
276
277 // Is the WM ICCCM supporting?
278 XGetWindowProperty(display, rootWnd,
5a2db5f1 279 _NET_SUPPORTING_WM_CHECK, 0, LONG_MAX,
8166ab43
VS
280 False, XA_WINDOW, &type, &format, &nwins,
281 &after, (unsigned char **)&wins);
282 if ( type != XA_WINDOW || nwins <= 0 || wins[0] == None )
283 return FALSE;
284 XFree(wins);
285
286 // Query for supported features:
287 XGetWindowProperty(display, rootWnd,
7f891ef1 288 _NET_SUPPORTED, 0, LONG_MAX,
8166ab43
VS
289 False, XA_ATOM, &type, &format, &natoms,
290 &after, (unsigned char **)&atoms);
291 if ( type != XA_ATOM || atoms == NULL )
292 return FALSE;
293
294 // Lookup the feature we want:
295 for (unsigned i = 0; i < natoms; i++)
296 {
297 if ( atoms[i] == feature )
298 {
299 XFree(atoms);
300 return TRUE;
301 }
302 }
303 XFree(atoms);
304 return FALSE;
305}
f618020a 306#endif
8166ab43
VS
307
308
309#define _NET_WM_STATE_REMOVE 0
310#define _NET_WM_STATE_ADD 1
311
312static void wxWMspecSetState(Display *display, Window rootWnd,
313 Window window, int operation, Atom state)
314{
315 wxMAKE_ATOM(_NET_WM_STATE, display);
316
317 if ( IsMapped(display, window) )
318 {
319 XEvent xev;
320 xev.type = ClientMessage;
321 xev.xclient.type = ClientMessage;
322 xev.xclient.serial = 0;
323 xev.xclient.send_event = True;
324 xev.xclient.display = display;
325 xev.xclient.window = window;
326 xev.xclient.message_type = _NET_WM_STATE;
327 xev.xclient.format = 32;
328 xev.xclient.data.l[0] = operation;
329 xev.xclient.data.l[1] = state;
330 xev.xclient.data.l[2] = None;
331
332 XSendEvent(display, rootWnd,
333 False,
334 SubstructureRedirectMask | SubstructureNotifyMask,
335 &xev);
336 }
337 // FIXME - must modify _NET_WM_STATE property list if the window
338 // wasn't mapped!
339}
340
341static void wxWMspecSetFullscreen(Display *display, Window rootWnd,
342 Window window, bool fullscreen)
343{
344 wxMAKE_ATOM(_NET_WM_STATE_FULLSCREEN, display);
345 wxWMspecSetState(display, rootWnd,
346 window,
347 fullscreen ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE,
348 _NET_WM_STATE_FULLSCREEN);
349}
350
351
352// Is the user running KDE's kwin window manager? At least kwin from KDE 3
353// sets KWIN_RUNNING property on the root window.
354static bool wxKwinRunning(Display *display, Window rootWnd)
355{
356 wxMAKE_ATOM(KWIN_RUNNING, display);
357
358 long *data;
359 Atom type;
360 int format;
361 unsigned long nitems, after;
362 if (XGetWindowProperty(display, rootWnd,
363 KWIN_RUNNING, 0, 1, False, KWIN_RUNNING,
364 &type, &format, &nitems, &after,
365 (unsigned char**)&data) != Success)
366 {
367 return FALSE;
368 }
369
370 bool retval = (type == KWIN_RUNNING &&
371 nitems == 1 && data && data[0] == 1);
372 XFree(data);
373 return retval;
374}
375
376// KDE's kwin is Qt-centric so much than no normal method of fullscreen
377// mode will work with it. We have to carefully emulate the Qt way.
378static void wxSetKDEFullscreen(Display *display, Window rootWnd,
379 Window w, bool fullscreen, wxRect *origRect)
380{
381 long data[2];
382 unsigned lng;
383
384 wxMAKE_ATOM(_NET_WM_WINDOW_TYPE, display);
385 wxMAKE_ATOM(_NET_WM_WINDOW_TYPE_NORMAL, display);
386 wxMAKE_ATOM(_KDE_NET_WM_WINDOW_TYPE_OVERRIDE, display);
387 wxMAKE_ATOM(_NET_WM_STATE_STAYS_ON_TOP, display);
388
389 if (fullscreen)
390 {
391 data[0] = _KDE_NET_WM_WINDOW_TYPE_OVERRIDE;
392 data[1] = _NET_WM_WINDOW_TYPE_NORMAL;
393 lng = 2;
394 }
395 else
396 {
397 data[0] = _NET_WM_WINDOW_TYPE_NORMAL;
398 data[1] = None;
399 lng = 1;
400 }
401
402 // it is neccessary to unmap the window, otherwise kwin will ignore us:
1cff04de
VS
403 XSync(display, False);
404
8166ab43
VS
405 bool wasMapped = IsMapped(display, w);
406 if (wasMapped)
1cff04de 407 {
8166ab43 408 XUnmapWindow(display, w);
1cff04de
VS
409 XSync(display, False);
410 }
411
8166ab43
VS
412 XChangeProperty(display, w, _NET_WM_WINDOW_TYPE, XA_ATOM, 32,
413 PropModeReplace, (unsigned char *) &data, lng);
1cff04de
VS
414 XSync(display, False);
415
8166ab43 416 if (wasMapped)
1cff04de 417 {
8166ab43 418 XMapRaised(display, w);
1cff04de
VS
419 XSync(display, False);
420 }
8166ab43
VS
421
422 wxWMspecSetState(display, rootWnd, w,
423 fullscreen ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE,
424 _NET_WM_STATE_STAYS_ON_TOP);
1cff04de 425 XSync(display, False);
8166ab43
VS
426
427 if (!fullscreen)
428 {
429 // NB: like many other WMs, kwin ignores first request for window
430 // position change after the window was mapped. This additional
431 // move+resize event will ensure that the window is restored in
432 // exactly same position as before it was made fullscreen (because
433 // wxTopLevelWindow::ShowFullScreen will call SetSize, thus
434 // setting the position for second time).
435 XMoveResizeWindow(display, w,
436 origRect->x, origRect->y,
437 origRect->width, origRect->height);
1cff04de 438 XSync(display, False);
8166ab43
VS
439 }
440}
441
442
443wxX11FullScreenMethod wxGetFullScreenMethodX11(WXDisplay* display,
444 WXWindow rootWindow)
445{
446 Window root = (Window)rootWindow;
447 Display *disp = (Display*)display;
448
449 // if WM supports _NET_WM_STATE_FULLSCREEN from wm-spec 1.2, use it:
450 wxMAKE_ATOM(_NET_WM_STATE_FULLSCREEN, disp);
451 if (wxQueryWMspecSupport(disp, root, _NET_WM_STATE_FULLSCREEN))
452 {
453 wxLogTrace(_T("fullscreen"),
454 _T("detected _NET_WM_STATE_FULLSCREEN support"));
455 return wxX11_FS_WMSPEC;
456 }
457
458 // if the user is running KDE's kwin WM, use a legacy hack because
459 // kwin doesn't understand any other method:
460 if (wxKwinRunning(disp, root))
461 {
462 wxLogTrace(_T("fullscreen"), _T("detected kwin"));
463 return wxX11_FS_KDE;
464 }
465
466 // finally, fall back to ICCCM heuristic method:
467 wxLogTrace(_T("fullscreen"), _T("unknown WM, using _WIN_LAYER"));
468 return wxX11_FS_GENERIC;
469}
470
471
472void wxSetFullScreenStateX11(WXDisplay* display, WXWindow rootWindow,
473 WXWindow window, bool show,
474 wxRect *origRect,
475 wxX11FullScreenMethod method)
476{
477 // NB: please see the comment under "Fullscreen mode:" title above
478 // for implications of changing this code.
479
480 Window wnd = (Window)window;
481 Window root = (Window)rootWindow;
482 Display *disp = (Display*)display;
483
484 if (method == wxX11_FS_AUTODETECT)
485 method = wxGetFullScreenMethodX11(display, rootWindow);
486
487 switch (method)
488 {
489 case wxX11_FS_WMSPEC:
490 wxWMspecSetFullscreen(disp, root, wnd, show);
491 break;
492 case wxX11_FS_KDE:
493 wxSetKDEFullscreen(disp, root, wnd, show, origRect);
494 break;
495 default:
496 wxWinHintsSetLayer(disp, root, wnd,
497 show ? WIN_LAYER_ABOVE_DOCK : WIN_LAYER_NORMAL);
498 break;
499 }
500}
501
502#endif
503