1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/unix/utilsx11.cpp
3 // Purpose: Miscellaneous X11 functions
4 // Author: Mattia Barbon, Vaclav Slavik, Robert Roebling
8 // Copyright: (c) wxWindows team
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 #if defined(__WXX11__) || defined(__WXGTK__) || defined(__WXMOTIF__)
14 // for compilers that support precompilation, includes "wx.h".
15 #include "wx/wxprec.h"
17 #include "wx/unix/utilsx11.h"
18 #include "wx/iconbndl.h"
24 #pragma message disable nosimpint
27 #include <X11/Xatom.h>
29 #pragma message enable nosimpint
37 // Various X11 Atoms used in this file:
38 static Atom _NET_WM_ICON
= 0;
39 static Atom _NET_WM_STATE
= 0;
40 static Atom _NET_WM_STATE_FULLSCREEN
= 0;
41 static Atom _NET_WM_STATE_STAYS_ON_TOP
= 0;
42 static Atom _NET_WM_WINDOW_TYPE
= 0;
43 static Atom _NET_WM_WINDOW_TYPE_NORMAL
= 0;
44 static Atom _KDE_NET_WM_WINDOW_TYPE_OVERRIDE
= 0;
45 static Atom _WIN_LAYER
= 0;
46 static Atom KWIN_RUNNING
= 0;
48 static Atom _NET_SUPPORTING_WM_CHECK
= 0;
49 static Atom _NET_SUPPORTED
= 0;
52 #define wxMAKE_ATOM(name, display) \
53 if (name == 0) name = XInternAtom((display), #name, False)
56 // Is the window mapped?
57 static bool IsMapped(Display
*display
, Window window
)
59 XWindowAttributes attr
;
60 XGetWindowAttributes(display
, window
, &attr
);
61 return (attr
.map_state
!= IsUnmapped
);
66 // Suspends X11 errors. Used when we expect errors but they are not fatal
68 class wxX11ErrorsSuspender
71 wxX11ErrorsSuspender(Display
*d
) : m_display(d
)
73 m_old
= XSetErrorHandler(handler
);
75 ~wxX11ErrorsSuspender()
78 XSetErrorHandler(m_old
);
82 int (*m_old
)(Display
*, XErrorEvent
*);
83 static int handler(Display
*, XErrorEvent
*) { return 0; }
88 // ----------------------------------------------------------------------------
89 // Setting icons for window manager:
90 // ----------------------------------------------------------------------------
92 void wxSetIconsX11( WXDisplay
* display
, WXWindow window
,
93 const wxIconBundle
& ib
)
97 size_t i
, max
= ib
.m_icons
.GetCount();
99 for( i
= 0; i
< max
; ++i
)
100 if( ib
.m_icons
[i
].Ok() )
101 size
+= 2 + ib
.m_icons
[i
].GetWidth() * ib
.m_icons
[i
].GetHeight();
103 wxMAKE_ATOM(_NET_WM_ICON
, (Display
*)display
);
107 // The code below is correct for 64-bit machines also.
108 // wxUint32* data = new wxUint32[size];
109 // wxUint32* ptr = data;
110 unsigned long* data
= new unsigned long[size
];
111 unsigned long* ptr
= data
;
113 for( i
= 0; i
< max
; ++i
)
115 const wxImage image
= ib
.m_icons
[i
].ConvertToImage();
116 int width
= image
.GetWidth(), height
= image
.GetHeight();
117 unsigned char* imageData
= image
.GetData();
118 unsigned char* imageDataEnd
= imageData
+ ( width
* height
* 3 );
119 bool hasMask
= image
.HasMask();
120 unsigned char rMask
, gMask
, bMask
;
121 unsigned char r
, g
, b
, a
;
125 rMask
= image
.GetMaskRed();
126 gMask
= image
.GetMaskGreen();
127 bMask
= image
.GetMaskBlue();
129 else // no mask, but still init the variables to avoid warnings
139 while( imageData
< imageDataEnd
) {
143 if( hasMask
&& r
== rMask
&& g
== gMask
&& b
== bMask
)
148 *ptr
++ = ( a
<< 24 ) | ( r
<< 16 ) | ( g
<< 8 ) | b
;
154 XChangeProperty( (Display
*)display
,
159 (unsigned char*)data
, size
);
164 XDeleteProperty( (Display
*)display
,
172 // ----------------------------------------------------------------------------
174 // ----------------------------------------------------------------------------
176 // NB: Setting fullscreen mode under X11 is a complicated matter. There was
177 // no standard way of doing it until recently. ICCCM doesn't know the
178 // concept of fullscreen windows and the only way to make a window
179 // fullscreen is to remove decorations, resize it to cover entire screen
180 // and set WIN_LAYER_ABOVE_DOCK.
182 // This doesn't always work, though. Specifically, at least kwin from
183 // KDE 3 ignores the hint. The only way to make kwin accept our request
184 // is to emulate the way Qt does it. That is, unmap the window, set
185 // _NET_WM_WINDOW_TYPE to _KDE_NET_WM_WINDOW_TYPE_OVERRIDE (KDE extension),
186 // add _NET_WM_STATE_STAYS_ON_TOP (ditto) to _NET_WM_STATE and map
189 // Version 1.2 of Window Manager Specification (aka wm-spec aka
190 // Extended Window Manager Hints) introduced _NET_WM_STATE_FULLSCREEN
191 // window state which provides cleanest and simplest possible way of
192 // making a window fullscreen. WM-spec is a de-facto standard adopted
193 // by GNOME and KDE folks, but _NET_WM_STATE_FULLSCREEN isn't yet widely
194 // supported. As of January 2003, only GNOME 2's default WM Metacity
195 // implements, KDE will support it from version 3.2. At toolkits level,
196 // GTK+ >= 2.1.2 uses it as the only method of making windows fullscreen
197 // (that's why wxGTK will *not* switch to using gtk_window_fullscreen
198 // unless it has better compatiblity with older WMs).
201 // This is what wxWindows does in wxSetFullScreenStateX11:
202 // 1) if _NET_WM_STATE_FULLSCREEN is supported, use it
203 // 2) otherwise try WM-specific hacks (KDE, IceWM)
204 // 3) use _WIN_LAYER and hope that the WM will recognize it
205 // The code was tested with:
206 // twm, IceWM, WindowMaker, Metacity, kwin, sawfish, lesstif-mwm
209 #define WIN_LAYER_NORMAL 4
210 #define WIN_LAYER_ABOVE_DOCK 10
212 static void wxWinHintsSetLayer(Display
*display
, Window rootWnd
,
213 Window window
, int layer
)
215 wxX11ErrorsSuspender
noerrors(display
);
219 wxMAKE_ATOM( _WIN_LAYER
, display
);
221 if (IsMapped(display
, window
))
223 xev
.type
= ClientMessage
;
224 xev
.xclient
.type
= ClientMessage
;
225 xev
.xclient
.window
= window
;
226 xev
.xclient
.message_type
= _WIN_LAYER
;
227 xev
.xclient
.format
= 32;
228 xev
.xclient
.data
.l
[0] = (long)layer
;
229 xev
.xclient
.data
.l
[1] = CurrentTime
;
231 XSendEvent(display
, rootWnd
, False
,
232 SubstructureNotifyMask
, (XEvent
*) &xev
);
239 XChangeProperty(display
, window
,
240 _WIN_LAYER
, XA_CARDINAL
, 32,
241 PropModeReplace
, (unsigned char *)data
, 1);
248 static bool wxQueryWMspecSupport(Display
* WXUNUSED(display
),
249 Window
WXUNUSED(rootWnd
),
252 GdkAtom gatom
= gdk_x11_xatom_to_atom(feature
);
253 return gdk_net_wm_supports(gatom
);
256 static bool wxQueryWMspecSupport(Display
*display
, Window rootWnd
, Atom feature
)
258 wxMAKE_ATOM(_NET_SUPPORTING_WM_CHECK
, display
);
259 wxMAKE_ATOM(_NET_SUPPORTED
, display
);
261 // FIXME: We may want to cache these checks. Note that we can't simply
262 // remember the results in global variable because the WM may go
263 // away and be replaced by another one! One possible approach
264 // would be invalidate the case every 15 seconds or so. Since this
265 // code is currently only used by wxTopLevelWindow::ShowFullScreen,
266 // it is not important that it is not optimized.
268 // If the WM supports ICCCM (i.e. the root window has
269 // _NET_SUPPORTING_WM_CHECK property that points to a WM-owned
270 // window), we could watch for DestroyNotify event on the window
271 // and invalidate our cache when the windows goes away (= WM
272 // is replaced by another one). This is what GTK+ 2 does.
273 // Let's do it only if it is needed, it requires changes to
281 unsigned long nwins
, natoms
;
283 // Is the WM ICCCM supporting?
284 XGetWindowProperty(display
, rootWnd
,
285 _NET_SUPPORTING_WM_CHECK
, 0, LONG_MAX
,
286 False
, XA_WINDOW
, &type
, &format
, &nwins
,
287 &after
, (unsigned char **)&wins
);
288 if ( type
!= XA_WINDOW
|| nwins
<= 0 || wins
[0] == None
)
292 // Query for supported features:
293 XGetWindowProperty(display
, rootWnd
,
294 _NET_SUPPORTED
, 0, LONG_MAX
,
295 False
, XA_ATOM
, &type
, &format
, &natoms
,
296 &after
, (unsigned char **)&atoms
);
297 if ( type
!= XA_ATOM
|| atoms
== NULL
)
300 // Lookup the feature we want:
301 for (unsigned i
= 0; i
< natoms
; i
++)
303 if ( atoms
[i
] == feature
)
315 #define _NET_WM_STATE_REMOVE 0
316 #define _NET_WM_STATE_ADD 1
318 static void wxWMspecSetState(Display
*display
, Window rootWnd
,
319 Window window
, int operation
, Atom state
)
321 wxMAKE_ATOM(_NET_WM_STATE
, display
);
323 if ( IsMapped(display
, window
) )
326 xev
.type
= ClientMessage
;
327 xev
.xclient
.type
= ClientMessage
;
328 xev
.xclient
.serial
= 0;
329 xev
.xclient
.send_event
= True
;
330 xev
.xclient
.display
= display
;
331 xev
.xclient
.window
= window
;
332 xev
.xclient
.message_type
= _NET_WM_STATE
;
333 xev
.xclient
.format
= 32;
334 xev
.xclient
.data
.l
[0] = operation
;
335 xev
.xclient
.data
.l
[1] = state
;
336 xev
.xclient
.data
.l
[2] = None
;
338 XSendEvent(display
, rootWnd
,
340 SubstructureRedirectMask
| SubstructureNotifyMask
,
343 // FIXME - must modify _NET_WM_STATE property list if the window
347 static void wxWMspecSetFullscreen(Display
*display
, Window rootWnd
,
348 Window window
, bool fullscreen
)
350 wxMAKE_ATOM(_NET_WM_STATE_FULLSCREEN
, display
);
351 wxWMspecSetState(display
, rootWnd
,
353 fullscreen
? _NET_WM_STATE_ADD
: _NET_WM_STATE_REMOVE
,
354 _NET_WM_STATE_FULLSCREEN
);
358 // Is the user running KDE's kwin window manager? At least kwin from KDE 3
359 // sets KWIN_RUNNING property on the root window.
360 static bool wxKwinRunning(Display
*display
, Window rootWnd
)
362 wxMAKE_ATOM(KWIN_RUNNING
, display
);
367 unsigned long nitems
, after
;
368 if (XGetWindowProperty(display
, rootWnd
,
369 KWIN_RUNNING
, 0, 1, False
, KWIN_RUNNING
,
370 &type
, &format
, &nitems
, &after
,
371 (unsigned char**)&data
) != Success
)
376 bool retval
= (type
== KWIN_RUNNING
&&
377 nitems
== 1 && data
&& data
[0] == 1);
382 // KDE's kwin is Qt-centric so much than no normal method of fullscreen
383 // mode will work with it. We have to carefully emulate the Qt way.
384 static void wxSetKDEFullscreen(Display
*display
, Window rootWnd
,
385 Window w
, bool fullscreen
, wxRect
*origRect
)
390 wxMAKE_ATOM(_NET_WM_WINDOW_TYPE
, display
);
391 wxMAKE_ATOM(_NET_WM_WINDOW_TYPE_NORMAL
, display
);
392 wxMAKE_ATOM(_KDE_NET_WM_WINDOW_TYPE_OVERRIDE
, display
);
393 wxMAKE_ATOM(_NET_WM_STATE_STAYS_ON_TOP
, display
);
397 data
[0] = _KDE_NET_WM_WINDOW_TYPE_OVERRIDE
;
398 data
[1] = _NET_WM_WINDOW_TYPE_NORMAL
;
403 data
[0] = _NET_WM_WINDOW_TYPE_NORMAL
;
408 // it is neccessary to unmap the window, otherwise kwin will ignore us:
409 XSync(display
, False
);
411 bool wasMapped
= IsMapped(display
, w
);
414 XUnmapWindow(display
, w
);
415 XSync(display
, False
);
418 XChangeProperty(display
, w
, _NET_WM_WINDOW_TYPE
, XA_ATOM
, 32,
419 PropModeReplace
, (unsigned char *) &data
, lng
);
420 XSync(display
, False
);
424 XMapRaised(display
, w
);
425 XSync(display
, False
);
428 wxWMspecSetState(display
, rootWnd
, w
,
429 fullscreen
? _NET_WM_STATE_ADD
: _NET_WM_STATE_REMOVE
,
430 _NET_WM_STATE_STAYS_ON_TOP
);
431 XSync(display
, False
);
435 // NB: like many other WMs, kwin ignores first request for window
436 // position change after the window was mapped. This additional
437 // move+resize event will ensure that the window is restored in
438 // exactly same position as before it was made fullscreen (because
439 // wxTopLevelWindow::ShowFullScreen will call SetSize, thus
440 // setting the position for second time).
441 XMoveResizeWindow(display
, w
,
442 origRect
->x
, origRect
->y
,
443 origRect
->width
, origRect
->height
);
444 XSync(display
, False
);
449 wxX11FullScreenMethod
wxGetFullScreenMethodX11(WXDisplay
* display
,
452 Window root
= (Window
)rootWindow
;
453 Display
*disp
= (Display
*)display
;
455 // if WM supports _NET_WM_STATE_FULLSCREEN from wm-spec 1.2, use it:
456 wxMAKE_ATOM(_NET_WM_STATE_FULLSCREEN
, disp
);
457 if (wxQueryWMspecSupport(disp
, root
, _NET_WM_STATE_FULLSCREEN
))
459 wxLogTrace(_T("fullscreen"),
460 _T("detected _NET_WM_STATE_FULLSCREEN support"));
461 return wxX11_FS_WMSPEC
;
464 // if the user is running KDE's kwin WM, use a legacy hack because
465 // kwin doesn't understand any other method:
466 if (wxKwinRunning(disp
, root
))
468 wxLogTrace(_T("fullscreen"), _T("detected kwin"));
472 // finally, fall back to ICCCM heuristic method:
473 wxLogTrace(_T("fullscreen"), _T("unknown WM, using _WIN_LAYER"));
474 return wxX11_FS_GENERIC
;
478 void wxSetFullScreenStateX11(WXDisplay
* display
, WXWindow rootWindow
,
479 WXWindow window
, bool show
,
481 wxX11FullScreenMethod method
)
483 // NB: please see the comment under "Fullscreen mode:" title above
484 // for implications of changing this code.
486 Window wnd
= (Window
)window
;
487 Window root
= (Window
)rootWindow
;
488 Display
*disp
= (Display
*)display
;
490 if (method
== wxX11_FS_AUTODETECT
)
491 method
= wxGetFullScreenMethodX11(display
, rootWindow
);
495 case wxX11_FS_WMSPEC
:
496 wxWMspecSetFullscreen(disp
, root
, wnd
, show
);
499 wxSetKDEFullscreen(disp
, root
, wnd
, show
, origRect
);
502 wxWinHintsSetLayer(disp
, root
, wnd
,
503 show
? WIN_LAYER_ABOVE_DOCK
: WIN_LAYER_NORMAL
);