]> git.saurik.com Git - wxWidgets.git/blame - src/unix/utilsx11.cpp
wxMac (and maybe others?) get PAGEUP/PAGEDOWN instead of NEXT/PRIOR
[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
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"
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
JJ
279#ifdef __VMS
280 _NET_SUPPORTING_WM_CHECK, 0, LONG_MAX,
281#else
8166ab43 282 _NET_SUPPORTING_WM_CHECK, 0, UINT_MAX,
5a2db5f1 283#endif
8166ab43
VS
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,
5a2db5f1
JJ
292#ifdef __VMS
293 _NET_SUPPORTED, 0, LONG_MAX,
294#else
295 _NET_SUPPORTED, 0, UINT_MAX,
296#endif
8166ab43
VS
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}
f618020a 314#endif
8166ab43
VS
315
316
317#define _NET_WM_STATE_REMOVE 0
318#define _NET_WM_STATE_ADD 1
319
320static 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
349static 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.
362static 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.
386static 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
438wxX11FullScreenMethod 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
467void 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