]> git.saurik.com Git - wxWidgets.git/blob - src/unix/utilsx11.cpp
wxMac (and maybe others?) get PAGEUP/PAGEDOWN instead of NEXT/PRIOR
[wxWidgets.git] / src / unix / utilsx11.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/unix/utilsx11.cpp
3 // Purpose: Miscellaneous X11 functions
4 // Author: Mattia Barbon, Vaclav Slavik, Robert Roebling
5 // Modified by:
6 // Created: 25.03.02
7 // RCS-ID: $Id$
8 // Copyright: (c) wxWindows team
9 // Licence: wxWindows license
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"
18 #include "wx/log.h"
19
20 #ifdef __VMS
21 #pragma message disable nosimpint
22 #endif
23 #include <X11/Xlib.h>
24 #include <X11/Xatom.h>
25 #ifdef __VMS
26 #pragma message enable nosimpint
27 #endif
28
29 #ifdef __WXGTK20__
30 #include <gdk/gdk.h>
31 #include <gdk/gdkx.h>
32 #endif
33
34 // Various X11 Atoms used in this file:
35 static Atom _NET_WM_ICON = 0;
36 static Atom _NET_WM_STATE = 0;
37 static Atom _NET_WM_STATE_FULLSCREEN = 0;
38 static Atom _NET_WM_STATE_STAYS_ON_TOP = 0;
39 static Atom _NET_WM_WINDOW_TYPE = 0;
40 static Atom _NET_WM_WINDOW_TYPE_NORMAL = 0;
41 static Atom _KDE_NET_WM_WINDOW_TYPE_OVERRIDE = 0;
42 static Atom _WIN_LAYER = 0;
43 static Atom KWIN_RUNNING = 0;
44 #ifndef __WXGTK20__
45 static Atom _NET_SUPPORTING_WM_CHECK = 0;
46 static 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?
54 static 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.
65 class wxX11ErrorsSuspender
66 {
67 public:
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 }
77 private:
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
89 void wxSetIconsX11( WXDisplay* display, WXWindow window,
90 const wxIconBundle& ib )
91 {
92 #if !wxUSE_NANOX
93 size_t size = 0;
94 size_t i, max = ib.m_icons.GetCount();
95
96 for( i = 0; i < max; ++i )
97 if( ib.m_icons[i].Ok() )
98 size += 2 + ib.m_icons[i].GetWidth() * ib.m_icons[i].GetHeight();
99
100 wxMAKE_ATOM(_NET_WM_ICON, (Display*)display);
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 }
123 else // no mask, but still init the variables to avoid warnings
124 {
125 rMask =
126 gMask =
127 bMask = 0;
128 }
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,
150 _NET_WM_ICON,
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,
160 _NET_WM_ICON );
161 }
162 #endif
163 }
164
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
206 static 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__
242 static 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
250 static 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,
279 #ifdef __VMS
280 _NET_SUPPORTING_WM_CHECK, 0, LONG_MAX,
281 #else
282 _NET_SUPPORTING_WM_CHECK, 0, UINT_MAX,
283 #endif
284 False, XA_WINDOW, &type, &format, &nwins,
285 &after, (unsigned char **)&wins);
286 if ( type != XA_WINDOW || nwins <= 0 || wins[0] == None )
287 return FALSE;
288 XFree(wins);
289
290 // Query for supported features:
291 XGetWindowProperty(display, rootWnd,
292 #ifdef __VMS
293 _NET_SUPPORTED, 0, LONG_MAX,
294 #else
295 _NET_SUPPORTED, 0, UINT_MAX,
296 #endif
297 False, XA_ATOM, &type, &format, &natoms,
298 &after, (unsigned char **)&atoms);
299 if ( type != XA_ATOM || atoms == NULL )
300 return FALSE;
301
302 // Lookup the feature we want:
303 for (unsigned i = 0; i < natoms; i++)
304 {
305 if ( atoms[i] == feature )
306 {
307 XFree(atoms);
308 return TRUE;
309 }
310 }
311 XFree(atoms);
312 return FALSE;
313 }
314 #endif
315
316
317 #define _NET_WM_STATE_REMOVE 0
318 #define _NET_WM_STATE_ADD 1
319
320 static void wxWMspecSetState(Display *display, Window rootWnd,
321 Window window, int operation, Atom state)
322 {
323 wxMAKE_ATOM(_NET_WM_STATE, display);
324
325 if ( IsMapped(display, window) )
326 {
327 XEvent xev;
328 xev.type = ClientMessage;
329 xev.xclient.type = ClientMessage;
330 xev.xclient.serial = 0;
331 xev.xclient.send_event = True;
332 xev.xclient.display = display;
333 xev.xclient.window = window;
334 xev.xclient.message_type = _NET_WM_STATE;
335 xev.xclient.format = 32;
336 xev.xclient.data.l[0] = operation;
337 xev.xclient.data.l[1] = state;
338 xev.xclient.data.l[2] = None;
339
340 XSendEvent(display, rootWnd,
341 False,
342 SubstructureRedirectMask | SubstructureNotifyMask,
343 &xev);
344 }
345 // FIXME - must modify _NET_WM_STATE property list if the window
346 // wasn't mapped!
347 }
348
349 static void wxWMspecSetFullscreen(Display *display, Window rootWnd,
350 Window window, bool fullscreen)
351 {
352 wxMAKE_ATOM(_NET_WM_STATE_FULLSCREEN, display);
353 wxWMspecSetState(display, rootWnd,
354 window,
355 fullscreen ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE,
356 _NET_WM_STATE_FULLSCREEN);
357 }
358
359
360 // Is the user running KDE's kwin window manager? At least kwin from KDE 3
361 // sets KWIN_RUNNING property on the root window.
362 static bool wxKwinRunning(Display *display, Window rootWnd)
363 {
364 wxMAKE_ATOM(KWIN_RUNNING, display);
365
366 long *data;
367 Atom type;
368 int format;
369 unsigned long nitems, after;
370 if (XGetWindowProperty(display, rootWnd,
371 KWIN_RUNNING, 0, 1, False, KWIN_RUNNING,
372 &type, &format, &nitems, &after,
373 (unsigned char**)&data) != Success)
374 {
375 return FALSE;
376 }
377
378 bool retval = (type == KWIN_RUNNING &&
379 nitems == 1 && data && data[0] == 1);
380 XFree(data);
381 return retval;
382 }
383
384 // KDE's kwin is Qt-centric so much than no normal method of fullscreen
385 // mode will work with it. We have to carefully emulate the Qt way.
386 static void wxSetKDEFullscreen(Display *display, Window rootWnd,
387 Window w, bool fullscreen, wxRect *origRect)
388 {
389 long data[2];
390 unsigned lng;
391
392 wxMAKE_ATOM(_NET_WM_WINDOW_TYPE, display);
393 wxMAKE_ATOM(_NET_WM_WINDOW_TYPE_NORMAL, display);
394 wxMAKE_ATOM(_KDE_NET_WM_WINDOW_TYPE_OVERRIDE, display);
395 wxMAKE_ATOM(_NET_WM_STATE_STAYS_ON_TOP, display);
396
397 if (fullscreen)
398 {
399 data[0] = _KDE_NET_WM_WINDOW_TYPE_OVERRIDE;
400 data[1] = _NET_WM_WINDOW_TYPE_NORMAL;
401 lng = 2;
402 }
403 else
404 {
405 data[0] = _NET_WM_WINDOW_TYPE_NORMAL;
406 data[1] = None;
407 lng = 1;
408 }
409
410 // it is neccessary to unmap the window, otherwise kwin will ignore us:
411 bool wasMapped = IsMapped(display, w);
412 if (wasMapped)
413 XUnmapWindow(display, w);
414 XChangeProperty(display, w, _NET_WM_WINDOW_TYPE, XA_ATOM, 32,
415 PropModeReplace, (unsigned char *) &data, lng);
416 if (wasMapped)
417 XMapRaised(display, w);
418
419 wxWMspecSetState(display, rootWnd, w,
420 fullscreen ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE,
421 _NET_WM_STATE_STAYS_ON_TOP);
422
423 if (!fullscreen)
424 {
425 // NB: like many other WMs, kwin ignores first request for window
426 // position change after the window was mapped. This additional
427 // move+resize event will ensure that the window is restored in
428 // exactly same position as before it was made fullscreen (because
429 // wxTopLevelWindow::ShowFullScreen will call SetSize, thus
430 // setting the position for second time).
431 XMoveResizeWindow(display, w,
432 origRect->x, origRect->y,
433 origRect->width, origRect->height);
434 }
435 }
436
437
438 wxX11FullScreenMethod wxGetFullScreenMethodX11(WXDisplay* display,
439 WXWindow rootWindow)
440 {
441 Window root = (Window)rootWindow;
442 Display *disp = (Display*)display;
443
444 // if WM supports _NET_WM_STATE_FULLSCREEN from wm-spec 1.2, use it:
445 wxMAKE_ATOM(_NET_WM_STATE_FULLSCREEN, disp);
446 if (wxQueryWMspecSupport(disp, root, _NET_WM_STATE_FULLSCREEN))
447 {
448 wxLogTrace(_T("fullscreen"),
449 _T("detected _NET_WM_STATE_FULLSCREEN support"));
450 return wxX11_FS_WMSPEC;
451 }
452
453 // if the user is running KDE's kwin WM, use a legacy hack because
454 // kwin doesn't understand any other method:
455 if (wxKwinRunning(disp, root))
456 {
457 wxLogTrace(_T("fullscreen"), _T("detected kwin"));
458 return wxX11_FS_KDE;
459 }
460
461 // finally, fall back to ICCCM heuristic method:
462 wxLogTrace(_T("fullscreen"), _T("unknown WM, using _WIN_LAYER"));
463 return wxX11_FS_GENERIC;
464 }
465
466
467 void wxSetFullScreenStateX11(WXDisplay* display, WXWindow rootWindow,
468 WXWindow window, bool show,
469 wxRect *origRect,
470 wxX11FullScreenMethod method)
471 {
472 // NB: please see the comment under "Fullscreen mode:" title above
473 // for implications of changing this code.
474
475 Window wnd = (Window)window;
476 Window root = (Window)rootWindow;
477 Display *disp = (Display*)display;
478
479 if (method == wxX11_FS_AUTODETECT)
480 method = wxGetFullScreenMethodX11(display, rootWindow);
481
482 switch (method)
483 {
484 case wxX11_FS_WMSPEC:
485 wxWMspecSetFullscreen(disp, root, wnd, show);
486 break;
487 case wxX11_FS_KDE:
488 wxSetKDEFullscreen(disp, root, wnd, show, origRect);
489 break;
490 default:
491 wxWinHintsSetLayer(disp, root, wnd,
492 show ? WIN_LAYER_ABOVE_DOCK : WIN_LAYER_NORMAL);
493 break;
494 }
495 }
496
497 #endif
498