1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/unix/utilsx11.cpp
3 // Purpose: Miscellaneous X11 functions
4 // Author: Mattia Barbon, Vaclav Slavik, Robert Roebling
8 // Copyright: (c) wxWidgets 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
69 static int wxX11ErrorsSuspender_handler(Display
*, XErrorEvent
*) { return 0; }
71 class wxX11ErrorsSuspender
74 wxX11ErrorsSuspender(Display
*d
) : m_display(d
)
76 m_old
= XSetErrorHandler(wxX11ErrorsSuspender_handler
);
78 ~wxX11ErrorsSuspender()
81 XSetErrorHandler(m_old
);
86 int (*m_old
)(Display
*, XErrorEvent
*);
91 // ----------------------------------------------------------------------------
92 // Setting icons for window manager:
93 // ----------------------------------------------------------------------------
95 void wxSetIconsX11( WXDisplay
* display
, WXWindow window
,
96 const wxIconBundle
& ib
)
100 size_t i
, max
= ib
.m_icons
.GetCount();
102 for( i
= 0; i
< max
; ++i
)
103 if( ib
.m_icons
[i
].Ok() )
104 size
+= 2 + ib
.m_icons
[i
].GetWidth() * ib
.m_icons
[i
].GetHeight();
106 wxMAKE_ATOM(_NET_WM_ICON
, (Display
*)display
);
110 // The code below is correct for 64-bit machines also.
111 // wxUint32* data = new wxUint32[size];
112 // wxUint32* ptr = data;
113 unsigned long* data
= new unsigned long[size
];
114 unsigned long* ptr
= data
;
116 for( i
= 0; i
< max
; ++i
)
118 const wxImage image
= ib
.m_icons
[i
].ConvertToImage();
119 int width
= image
.GetWidth(), height
= image
.GetHeight();
120 unsigned char* imageData
= image
.GetData();
121 unsigned char* imageDataEnd
= imageData
+ ( width
* height
* 3 );
122 bool hasMask
= image
.HasMask();
123 unsigned char rMask
, gMask
, bMask
;
124 unsigned char r
, g
, b
, a
;
128 rMask
= image
.GetMaskRed();
129 gMask
= image
.GetMaskGreen();
130 bMask
= image
.GetMaskBlue();
132 else // no mask, but still init the variables to avoid warnings
142 while( imageData
< imageDataEnd
) {
146 if( hasMask
&& r
== rMask
&& g
== gMask
&& b
== bMask
)
151 *ptr
++ = ( a
<< 24 ) | ( r
<< 16 ) | ( g
<< 8 ) | b
;
157 XChangeProperty( (Display
*)display
,
162 (unsigned char*)data
, size
);
167 XDeleteProperty( (Display
*)display
,
175 // ----------------------------------------------------------------------------
177 // ----------------------------------------------------------------------------
179 // NB: Setting fullscreen mode under X11 is a complicated matter. There was
180 // no standard way of doing it until recently. ICCCM doesn't know the
181 // concept of fullscreen windows and the only way to make a window
182 // fullscreen is to remove decorations, resize it to cover entire screen
183 // and set WIN_LAYER_ABOVE_DOCK.
185 // This doesn't always work, though. Specifically, at least kwin from
186 // KDE 3 ignores the hint. The only way to make kwin accept our request
187 // is to emulate the way Qt does it. That is, unmap the window, set
188 // _NET_WM_WINDOW_TYPE to _KDE_NET_WM_WINDOW_TYPE_OVERRIDE (KDE extension),
189 // add _NET_WM_STATE_STAYS_ON_TOP (ditto) to _NET_WM_STATE and map
192 // Version 1.2 of Window Manager Specification (aka wm-spec aka
193 // Extended Window Manager Hints) introduced _NET_WM_STATE_FULLSCREEN
194 // window state which provides cleanest and simplest possible way of
195 // making a window fullscreen. WM-spec is a de-facto standard adopted
196 // by GNOME and KDE folks, but _NET_WM_STATE_FULLSCREEN isn't yet widely
197 // supported. As of January 2003, only GNOME 2's default WM Metacity
198 // implements, KDE will support it from version 3.2. At toolkits level,
199 // GTK+ >= 2.1.2 uses it as the only method of making windows fullscreen
200 // (that's why wxGTK will *not* switch to using gtk_window_fullscreen
201 // unless it has better compatiblity with older WMs).
204 // This is what wxWidgets does in wxSetFullScreenStateX11:
205 // 1) if _NET_WM_STATE_FULLSCREEN is supported, use it
206 // 2) otherwise try WM-specific hacks (KDE, IceWM)
207 // 3) use _WIN_LAYER and hope that the WM will recognize it
208 // The code was tested with:
209 // twm, IceWM, WindowMaker, Metacity, kwin, sawfish, lesstif-mwm
212 #define WIN_LAYER_NORMAL 4
213 #define WIN_LAYER_ABOVE_DOCK 10
215 static void wxWinHintsSetLayer(Display
*display
, Window rootWnd
,
216 Window window
, int layer
)
218 wxX11ErrorsSuspender
noerrors(display
);
222 wxMAKE_ATOM( _WIN_LAYER
, display
);
224 if (IsMapped(display
, window
))
226 xev
.type
= ClientMessage
;
227 xev
.xclient
.type
= ClientMessage
;
228 xev
.xclient
.window
= window
;
229 xev
.xclient
.message_type
= _WIN_LAYER
;
230 xev
.xclient
.format
= 32;
231 xev
.xclient
.data
.l
[0] = (long)layer
;
232 xev
.xclient
.data
.l
[1] = CurrentTime
;
234 XSendEvent(display
, rootWnd
, False
,
235 SubstructureNotifyMask
, (XEvent
*) &xev
);
242 XChangeProperty(display
, window
,
243 _WIN_LAYER
, XA_CARDINAL
, 32,
244 PropModeReplace
, (unsigned char *)data
, 1);
251 static bool wxQueryWMspecSupport(Display
* WXUNUSED(display
),
252 Window
WXUNUSED(rootWnd
),
255 GdkAtom gatom
= gdk_x11_xatom_to_atom(feature
);
256 return gdk_net_wm_supports(gatom
);
259 static bool wxQueryWMspecSupport(Display
*display
, Window rootWnd
, Atom feature
)
261 wxMAKE_ATOM(_NET_SUPPORTING_WM_CHECK
, display
);
262 wxMAKE_ATOM(_NET_SUPPORTED
, display
);
264 // FIXME: We may want to cache these checks. Note that we can't simply
265 // remember the results in global variable because the WM may go
266 // away and be replaced by another one! One possible approach
267 // would be invalidate the case every 15 seconds or so. Since this
268 // code is currently only used by wxTopLevelWindow::ShowFullScreen,
269 // it is not important that it is not optimized.
271 // If the WM supports ICCCM (i.e. the root window has
272 // _NET_SUPPORTING_WM_CHECK property that points to a WM-owned
273 // window), we could watch for DestroyNotify event on the window
274 // and invalidate our cache when the windows goes away (= WM
275 // is replaced by another one). This is what GTK+ 2 does.
276 // Let's do it only if it is needed, it requires changes to
284 unsigned long nwins
, natoms
;
286 // Is the WM ICCCM supporting?
287 XGetWindowProperty(display
, rootWnd
,
288 _NET_SUPPORTING_WM_CHECK
, 0, LONG_MAX
,
289 False
, XA_WINDOW
, &type
, &format
, &nwins
,
290 &after
, (unsigned char **)&wins
);
291 if ( type
!= XA_WINDOW
|| nwins
<= 0 || wins
[0] == None
)
295 // Query for supported features:
296 XGetWindowProperty(display
, rootWnd
,
297 _NET_SUPPORTED
, 0, LONG_MAX
,
298 False
, XA_ATOM
, &type
, &format
, &natoms
,
299 &after
, (unsigned char **)&atoms
);
300 if ( type
!= XA_ATOM
|| atoms
== NULL
)
303 // Lookup the feature we want:
304 for (unsigned i
= 0; i
< natoms
; i
++)
306 if ( atoms
[i
] == feature
)
318 #define _NET_WM_STATE_REMOVE 0
319 #define _NET_WM_STATE_ADD 1
321 static void wxWMspecSetState(Display
*display
, Window rootWnd
,
322 Window window
, int operation
, Atom state
)
324 wxMAKE_ATOM(_NET_WM_STATE
, display
);
326 if ( IsMapped(display
, window
) )
329 xev
.type
= ClientMessage
;
330 xev
.xclient
.type
= ClientMessage
;
331 xev
.xclient
.serial
= 0;
332 xev
.xclient
.send_event
= True
;
333 xev
.xclient
.display
= display
;
334 xev
.xclient
.window
= window
;
335 xev
.xclient
.message_type
= _NET_WM_STATE
;
336 xev
.xclient
.format
= 32;
337 xev
.xclient
.data
.l
[0] = operation
;
338 xev
.xclient
.data
.l
[1] = state
;
339 xev
.xclient
.data
.l
[2] = None
;
341 XSendEvent(display
, rootWnd
,
343 SubstructureRedirectMask
| SubstructureNotifyMask
,
346 // FIXME - must modify _NET_WM_STATE property list if the window
350 static void wxWMspecSetFullscreen(Display
*display
, Window rootWnd
,
351 Window window
, bool fullscreen
)
353 wxMAKE_ATOM(_NET_WM_STATE_FULLSCREEN
, display
);
354 wxWMspecSetState(display
, rootWnd
,
356 fullscreen
? _NET_WM_STATE_ADD
: _NET_WM_STATE_REMOVE
,
357 _NET_WM_STATE_FULLSCREEN
);
361 // Is the user running KDE's kwin window manager? At least kwin from KDE 3
362 // sets KWIN_RUNNING property on the root window.
363 static bool wxKwinRunning(Display
*display
, Window rootWnd
)
365 wxMAKE_ATOM(KWIN_RUNNING
, display
);
370 unsigned long nitems
, after
;
371 if (XGetWindowProperty(display
, rootWnd
,
372 KWIN_RUNNING
, 0, 1, False
, KWIN_RUNNING
,
373 &type
, &format
, &nitems
, &after
,
374 (unsigned char**)&data
) != Success
)
379 bool retval
= (type
== KWIN_RUNNING
&&
380 nitems
== 1 && data
&& data
[0] == 1);
385 // KDE's kwin is Qt-centric so much than no normal method of fullscreen
386 // mode will work with it. We have to carefully emulate the Qt way.
387 static void wxSetKDEFullscreen(Display
*display
, Window rootWnd
,
388 Window w
, bool fullscreen
, wxRect
*origRect
)
393 wxMAKE_ATOM(_NET_WM_WINDOW_TYPE
, display
);
394 wxMAKE_ATOM(_NET_WM_WINDOW_TYPE_NORMAL
, display
);
395 wxMAKE_ATOM(_KDE_NET_WM_WINDOW_TYPE_OVERRIDE
, display
);
396 wxMAKE_ATOM(_NET_WM_STATE_STAYS_ON_TOP
, display
);
400 data
[0] = _KDE_NET_WM_WINDOW_TYPE_OVERRIDE
;
401 data
[1] = _NET_WM_WINDOW_TYPE_NORMAL
;
406 data
[0] = _NET_WM_WINDOW_TYPE_NORMAL
;
411 // it is neccessary to unmap the window, otherwise kwin will ignore us:
412 XSync(display
, False
);
414 bool wasMapped
= IsMapped(display
, w
);
417 XUnmapWindow(display
, w
);
418 XSync(display
, False
);
421 XChangeProperty(display
, w
, _NET_WM_WINDOW_TYPE
, XA_ATOM
, 32,
422 PropModeReplace
, (unsigned char *) &data
, lng
);
423 XSync(display
, False
);
427 XMapRaised(display
, w
);
428 XSync(display
, False
);
431 wxWMspecSetState(display
, rootWnd
, w
,
432 fullscreen
? _NET_WM_STATE_ADD
: _NET_WM_STATE_REMOVE
,
433 _NET_WM_STATE_STAYS_ON_TOP
);
434 XSync(display
, False
);
438 // NB: like many other WMs, kwin ignores first request for window
439 // position change after the window was mapped. This additional
440 // move+resize event will ensure that the window is restored in
441 // exactly same position as before it was made fullscreen (because
442 // wxTopLevelWindow::ShowFullScreen will call SetSize, thus
443 // setting the position for second time).
444 XMoveResizeWindow(display
, w
,
445 origRect
->x
, origRect
->y
,
446 origRect
->width
, origRect
->height
);
447 XSync(display
, False
);
452 wxX11FullScreenMethod
wxGetFullScreenMethodX11(WXDisplay
* display
,
455 Window root
= (Window
)rootWindow
;
456 Display
*disp
= (Display
*)display
;
458 // if WM supports _NET_WM_STATE_FULLSCREEN from wm-spec 1.2, use it:
459 wxMAKE_ATOM(_NET_WM_STATE_FULLSCREEN
, disp
);
460 if (wxQueryWMspecSupport(disp
, root
, _NET_WM_STATE_FULLSCREEN
))
462 wxLogTrace(_T("fullscreen"),
463 _T("detected _NET_WM_STATE_FULLSCREEN support"));
464 return wxX11_FS_WMSPEC
;
467 // if the user is running KDE's kwin WM, use a legacy hack because
468 // kwin doesn't understand any other method:
469 if (wxKwinRunning(disp
, root
))
471 wxLogTrace(_T("fullscreen"), _T("detected kwin"));
475 // finally, fall back to ICCCM heuristic method:
476 wxLogTrace(_T("fullscreen"), _T("unknown WM, using _WIN_LAYER"));
477 return wxX11_FS_GENERIC
;
481 void wxSetFullScreenStateX11(WXDisplay
* display
, WXWindow rootWindow
,
482 WXWindow window
, bool show
,
484 wxX11FullScreenMethod method
)
486 // NB: please see the comment under "Fullscreen mode:" title above
487 // for implications of changing this code.
489 Window wnd
= (Window
)window
;
490 Window root
= (Window
)rootWindow
;
491 Display
*disp
= (Display
*)display
;
493 if (method
== wxX11_FS_AUTODETECT
)
494 method
= wxGetFullScreenMethodX11(display
, rootWindow
);
498 case wxX11_FS_WMSPEC
:
499 wxWMspecSetFullscreen(disp
, root
, wnd
, show
);
502 wxSetKDEFullscreen(disp
, root
, wnd
, show
, origRect
);
505 wxWinHintsSetLayer(disp
, root
, wnd
,
506 show
? WIN_LAYER_ABOVE_DOCK
: WIN_LAYER_NORMAL
);
513 // ----------------------------------------------------------------------------
514 // keycode translations
515 // ----------------------------------------------------------------------------
517 #include <X11/keysym.h>
519 // FIXME what about tables??
521 int wxCharCodeXToWX(KeySym keySym
)
528 id
= WXK_SHIFT
; break;
531 id
= WXK_CONTROL
; break;
536 id
= WXK_BACK
; break;
538 id
= WXK_DELETE
; break;
540 id
= WXK_CLEAR
; break;
546 id
= WXK_RETURN
; break;
548 id
= WXK_ESCAPE
; break;
551 id
= WXK_PAUSE
; break;
553 id
= WXK_NUMLOCK
; break;
555 id
= WXK_SCROLL
; break;
558 id
= WXK_HOME
; break;
562 id
= WXK_LEFT
; break;
564 id
= WXK_RIGHT
; break;
568 id
= WXK_DOWN
; break;
570 id
= WXK_NEXT
; break;
572 id
= WXK_PRIOR
; break;
574 id
= WXK_MENU
; break;
576 id
= WXK_SELECT
; break;
578 id
= WXK_CANCEL
; break;
580 id
= WXK_PRINT
; break;
582 id
= WXK_EXECUTE
; break;
584 id
= WXK_INSERT
; break;
586 id
= WXK_HELP
; break;
589 id
= WXK_MULTIPLY
; break;
593 id
= WXK_SUBTRACT
; break;
595 id
= WXK_DIVIDE
; break;
597 id
= WXK_DECIMAL
; break;
605 id
= WXK_RETURN
; break;
607 id
= WXK_NUMPAD0
; break;
609 id
= WXK_NUMPAD1
; break;
611 id
= WXK_NUMPAD2
; break;
613 id
= WXK_NUMPAD3
; break;
615 id
= WXK_NUMPAD4
; break;
617 id
= WXK_NUMPAD5
; break;
619 id
= WXK_NUMPAD6
; break;
621 id
= WXK_NUMPAD7
; break;
623 id
= WXK_NUMPAD8
; break;
625 id
= WXK_NUMPAD9
; break;
675 id
= (keySym
<= 255) ? (int)keySym
: -1;
681 KeySym
wxCharCodeWXToX(int id
)
687 case WXK_CANCEL
: keySym
= XK_Cancel
; break;
688 case WXK_BACK
: keySym
= XK_BackSpace
; break;
689 case WXK_TAB
: keySym
= XK_Tab
; break;
690 case WXK_CLEAR
: keySym
= XK_Clear
; break;
691 case WXK_RETURN
: keySym
= XK_Return
; break;
692 case WXK_SHIFT
: keySym
= XK_Shift_L
; break;
693 case WXK_CONTROL
: keySym
= XK_Control_L
; break;
694 case WXK_ALT
: keySym
= XK_Meta_L
; break;
695 case WXK_MENU
: keySym
= XK_Menu
; break;
696 case WXK_PAUSE
: keySym
= XK_Pause
; break;
697 case WXK_ESCAPE
: keySym
= XK_Escape
; break;
698 case WXK_SPACE
: keySym
= ' '; break;
699 case WXK_PRIOR
: keySym
= XK_Prior
; break;
700 case WXK_NEXT
: keySym
= XK_Next
; break;
701 case WXK_END
: keySym
= XK_End
; break;
702 case WXK_HOME
: keySym
= XK_Home
; break;
703 case WXK_LEFT
: keySym
= XK_Left
; break;
704 case WXK_UP
: keySym
= XK_Up
; break;
705 case WXK_RIGHT
: keySym
= XK_Right
; break;
706 case WXK_DOWN
: keySym
= XK_Down
; break;
707 case WXK_SELECT
: keySym
= XK_Select
; break;
708 case WXK_PRINT
: keySym
= XK_Print
; break;
709 case WXK_EXECUTE
: keySym
= XK_Execute
; break;
710 case WXK_INSERT
: keySym
= XK_Insert
; break;
711 case WXK_DELETE
: keySym
= XK_Delete
; break;
712 case WXK_HELP
: keySym
= XK_Help
; break;
713 case WXK_NUMPAD0
: keySym
= XK_KP_0
; break;
714 case WXK_NUMPAD1
: keySym
= XK_KP_1
; break;
715 case WXK_NUMPAD2
: keySym
= XK_KP_2
; break;
716 case WXK_NUMPAD3
: keySym
= XK_KP_3
; break;
717 case WXK_NUMPAD4
: keySym
= XK_KP_4
; break;
718 case WXK_NUMPAD5
: keySym
= XK_KP_5
; break;
719 case WXK_NUMPAD6
: keySym
= XK_KP_6
; break;
720 case WXK_NUMPAD7
: keySym
= XK_KP_7
; break;
721 case WXK_NUMPAD8
: keySym
= XK_KP_8
; break;
722 case WXK_NUMPAD9
: keySym
= XK_KP_9
; break;
723 case WXK_MULTIPLY
: keySym
= XK_KP_Multiply
; break;
724 case WXK_ADD
: keySym
= XK_KP_Add
; break;
725 case WXK_SUBTRACT
: keySym
= XK_KP_Subtract
; break;
726 case WXK_DECIMAL
: keySym
= XK_KP_Decimal
; break;
727 case WXK_DIVIDE
: keySym
= XK_KP_Divide
; break;
728 case WXK_F1
: keySym
= XK_F1
; break;
729 case WXK_F2
: keySym
= XK_F2
; break;
730 case WXK_F3
: keySym
= XK_F3
; break;
731 case WXK_F4
: keySym
= XK_F4
; break;
732 case WXK_F5
: keySym
= XK_F5
; break;
733 case WXK_F6
: keySym
= XK_F6
; break;
734 case WXK_F7
: keySym
= XK_F7
; break;
735 case WXK_F8
: keySym
= XK_F8
; break;
736 case WXK_F9
: keySym
= XK_F9
; break;
737 case WXK_F10
: keySym
= XK_F10
; break;
738 case WXK_F11
: keySym
= XK_F11
; break;
739 case WXK_F12
: keySym
= XK_F12
; break;
740 case WXK_F13
: keySym
= XK_F13
; break;
741 case WXK_F14
: keySym
= XK_F14
; break;
742 case WXK_F15
: keySym
= XK_F15
; break;
743 case WXK_F16
: keySym
= XK_F16
; break;
744 case WXK_F17
: keySym
= XK_F17
; break;
745 case WXK_F18
: keySym
= XK_F18
; break;
746 case WXK_F19
: keySym
= XK_F19
; break;
747 case WXK_F20
: keySym
= XK_F20
; break;
748 case WXK_F21
: keySym
= XK_F21
; break;
749 case WXK_F22
: keySym
= XK_F22
; break;
750 case WXK_F23
: keySym
= XK_F23
; break;
751 case WXK_F24
: keySym
= XK_F24
; break;
752 case WXK_NUMLOCK
: keySym
= XK_Num_Lock
; break;
753 case WXK_SCROLL
: keySym
= XK_Scroll_Lock
; break;
754 default: keySym
= id
<= 255 ? (KeySym
)id
: 0;
761 // ----------------------------------------------------------------------------
762 // check current state of a key
763 // ----------------------------------------------------------------------------
767 bool wxGetKeyState(wxKeyCode key
)
769 wxASSERT_MSG(key
!= WXK_LBUTTON
&& key
!= WXK_RBUTTON
&& key
!=
770 WXK_MBUTTON
, wxT("can't use wxGetKeyState() for mouse buttons"));
772 #if defined(__WXX11__)
773 Display
*pDisplay
= (Display
*) wxApp::GetDisplay();
774 #elif defined(__WXGTK__)
775 Display
*pDisplay
= GDK_DISPLAY();
776 #elif defined(__WXMOTIF__)
777 Display
*pDisplay
= (Display
*) (wxTheApp
? wxTheApp
->GetInitialDisplay() : NULL
);
779 #error Add code to get the DISPLAY for this platform
782 int iKey
= wxCharCodeWXToX(key
);
784 Window wDummy1
, wDummy2
;
785 int iDummy3
, iDummy4
, iDummy5
, iDummy6
;
787 XModifierKeymap
* map
= XGetModifierMapping(pDisplay
);
788 KeyCode keyCode
= XKeysymToKeycode(pDisplay
,iKey
);
789 if (keyCode
== NoSymbol
)
792 for (int i
= 0; i
< 8; ++i
)
794 if ( map
->modifiermap
[map
->max_keypermod
* i
] == keyCode
)
800 XQueryPointer(pDisplay
, DefaultRootWindow(pDisplay
), &wDummy1
, &wDummy2
,
801 &iDummy3
, &iDummy4
, &iDummy5
, &iDummy6
, &iMask
);
802 XFreeModifiermap(map
);
803 return (iMask
& iKeyMask
) != 0;