1 /* This file is part of the KDE libraries
2 Copyright (C) 2001,2002 Ellis Whitehead <ellis@kde.org>
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
20 #include "kglobalaccel_x11.h"
22 #include <QtGui/QWidgetList>
25 #include "globalshortcutsregistry.h"
26 #include "kkeyserver_x11.h"
28 #include <kapplication.h>
31 #include <QtCore/QRegExp>
32 #include <QtGui/QWidget>
33 #include <QtCore/QMetaClassInfo>
34 #include <QtGui/QMenu>
36 #include <kxerrorhandler.h>
40 #include <X11/Xutil.h>
41 #include <X11/keysym.h>
45 static int XGrabErrorHandler( Display
*, XErrorEvent
*e
) {
46 if ( e
->error_code
!= BadAccess
) {
47 kWarning() << "grabKey: got X error " << e
->type
<< " instead of BadAccess\n";
54 // mask of modifiers which can be used in shortcuts
55 // (meta, alt, ctrl, shift)
56 // g_keyModMaskXOnOrOff
57 // mask of modifiers where we don't care whether they are on or off
58 // (caps lock, num lock, scroll lock)
59 static uint g_keyModMaskXAccel
= 0;
60 static uint g_keyModMaskXOnOrOff
= 0;
62 static void calculateGrabMasks()
64 g_keyModMaskXAccel
= KKeyServer::accelModMaskX();
65 g_keyModMaskXOnOrOff
=
66 KKeyServer::modXLock() |
67 KKeyServer::modXNumLock() |
68 KKeyServer::modXScrollLock() |
69 KKeyServer::modXModeSwitch();
70 //kDebug() << "g_keyModMaskXAccel = " << g_keyModMaskXAccel
71 // << "g_keyModMaskXOnOrOff = " << g_keyModMaskXOnOrOff << endl;
74 //----------------------------------------------------
76 KGlobalAccelImpl::KGlobalAccelImpl(GlobalShortcutsRegistry
*owner
)
82 bool KGlobalAccelImpl::grabKey( int keyQt
, bool grab
)
85 kDebug() << "Tried to grab key with null code.";
91 KKeyServer::keyQtToCodeX(keyQt
, &keyCodeX
);
92 KKeyServer::keyQtToModX(keyQt
, &keyModX
);
94 keyModX
&= g_keyModMaskXAccel
; // Get rid of any non-relevant bits in mod
96 // HACK: make Alt+Print work
97 // only do this for the Xorg default keyboard keycodes,
98 // other mappings (e.g. evdev) don't need or want it
99 if( keyCodeX
== XK_Sys_Req
&& XKeycodeToKeysym( QX11Info::display(), 111, 0 ) == XK_Print
) {
100 keyModX
|= KKeyServer::modXAlt();
107 // kDebug() << "grabKey keyQt " << (keyQt & ~Qt::KeyboardModifierMask)
108 // << " mod " << (keyQt & Qt::KeyboardModifierMask) << " ( key: '" << QKeySequence(keyQt).toString()
109 // << "', grab: " << grab << " ): keyCodeX: " << keyCodeX << " keyModX: " << keyModX << endl;
111 KXErrorHandler
handler( XGrabErrorHandler
);
113 // We'll have to grab 8 key modifier combinations in order to cover all
114 // combinations of CapsLock, NumLock, ScrollLock.
115 // Does anyone with more X-savvy know how to set a mask on QX11Info::appRootWindow so that
116 // the irrelevant bits are always ignored and we can just make one XGrabKey
117 // call per accelerator? -- ellis
119 QString sDebug
= QString("\tcode: 0x%1 state: 0x%2 | ").arg(keyCodeX
,0,16).arg(keyModX
,0,16);
121 uint keyModMaskX
= ~g_keyModMaskXOnOrOff
;
122 for( uint irrelevantBitsMask
= 0; irrelevantBitsMask
<= 0xff; irrelevantBitsMask
++ ) {
123 if( (irrelevantBitsMask
& keyModMaskX
) == 0 ) {
125 sDebug
+= QString("0x%3, ").arg(irrelevantBitsMask
, 0, 16);
128 XGrabKey( QX11Info::display(), keyCodeX
, keyModX
| irrelevantBitsMask
,
129 QX11Info::appRootWindow(), True
, GrabModeAsync
, GrabModeSync
);
131 XUngrabKey( QX11Info::display(), keyCodeX
, keyModX
| irrelevantBitsMask
, QX11Info::appRootWindow() );
137 failed
= handler
.error( true ); // sync now
139 kDebug() << "grab failed!\n";
140 for( uint m
= 0; m
<= 0xff; m
++ ) {
141 if(( m
& keyModMaskX
) == 0 )
142 XUngrabKey( QX11Info::display(), keyCodeX
, keyModX
| m
, QX11Info::appRootWindow() );
150 bool KGlobalAccelImpl::x11Event( XEvent
* event
)
152 switch( event
->type
) {
154 XRefreshKeyboardMapping(&event
->xmapping
);
159 if( x11KeyPress( event
) )
166 void KGlobalAccelImpl::x11MappingNotify()
168 // Maybe the X modifier map has been changed.
169 // uint oldKeyModMaskXAccel = g_keyModMaskXAccel;
170 // uint oldKeyModMaskXOnOrOff = g_keyModMaskXOnOrOff;
172 KKeyServer::initializeMods();
173 calculateGrabMasks();
175 #if 0 //### investigate!
176 if (oldKeyModMaskXAccel
!= g_keyModMaskXAccel
|| oldKeyModMaskXOnOrOff
!= g_keyModMaskXOnOrOff
)
177 // Do new XGrabKey()s.
178 m_owner
->regrabKeys();
182 bool KGlobalAccelImpl::x11KeyPress( const XEvent
*pEvent
)
184 // ATTENTION: SECURITY
186 // This method should NEVER EVER print out the key it received as debug
187 // output. kpwasswdserver is a kded module too. So we get key events from
188 // it too. Which means we would print out all passwords.
190 // We are only allowed to print out the key received AFTER we made sure
191 // it is a global shortcut. KGlobalAccelImpl cannot check this.
192 // GlobalShortcutsRegistry will do that.
194 // Keyboard needs to be ungrabed after XGrabKey() activates the grab, otherwise
195 // it becomes frozen. There is a chance this will ungrab even when it should
196 // not, if some code calls XGrabKeyboard() directly, but doing so in kded
197 // should be very unlikely, and probably stupid.
198 // If this code is again moved out of kded for some reason, this needs
199 // to be revisited (I'm pretty sure this used to break KWin).
200 if( !QWidget::keyboardGrabber() && !QApplication::activePopupWidget()) {
201 XUngrabKeyboard( QX11Info::display(), pEvent
->xkey
.time
);
202 XFlush( QX11Info::display()); // avoid X(?) bug
205 uchar keyCodeX
= pEvent
->xkey
.keycode
;
206 uint keyModX
= pEvent
->xkey
.state
& (g_keyModMaskXAccel
| KKeyServer::MODE_SWITCH
);
209 XLookupString( (XKeyEvent
*) pEvent
, 0, 0, &keySym
, 0 );
210 uint keySymX
= (uint
)keySym
;
212 // If numlock is active and a keypad key is pressed, XOR the SHIFT state.
213 // e.g., KP_4 => Shift+KP_Left, and Shift+KP_4 => KP_Left.
214 if( pEvent
->xkey
.state
& KKeyServer::modXNumLock() ) {
215 uint sym
= XKeycodeToKeysym( QX11Info::display(), keyCodeX
, 0 );
216 // If this is a keypad key,
217 if( sym
>= XK_KP_Space
&& sym
<= XK_KP_9
) {
219 // Leave the following keys unaltered
220 // FIXME: The proper solution is to see which keysyms don't change when shifted.
227 keyModX
^= KKeyServer::modXShift();
234 KKeyServer::symXToKeyQt(keySymX
, &keyCodeQt
);
235 KKeyServer::modXToQt(keyModX
, &keyModQt
);
237 int keyQt
= keyCodeQt
| keyModQt
;
239 // All that work for this hey... argh...
240 if (m_owner
->keyPressed(keyQt
))
246 void KGlobalAccelImpl::setEnabled( bool enable
)
249 kapp
->installX11EventFilter( this );
251 kapp
->removeX11EventFilter( this );
255 #include "kglobalaccel_x11.moc"