summaryrefslogblamecommitdiffstats
path: root/src/input/x11FakeKeyboardHandler.cpp
blob: f27ea1e5cfcbe595c093422b4496392ae7cf0762 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11










                                                                               
                              









                                                                             
                                   





                                 




                                    


                                   
 

                                                                        
 
                                                              










                           





































                                                                    
                                          













                                              








                                                                         
 



                                                         

                                            
                                                   
































































                                                     
 






                                            






                                                                  

                                                













                                                                  







                                                          



                                                                
                                                            


















                                                                          
                                            
                                            
                                              


                                                 








                                                     
 




                                                             
 


                                                                    


                                                                    
 




                                                            
 





























                                                                                                                                             





























                                                                                                 

                                        
 










                                                                        
                                               
                                    
                                         
         
                            

                                                







                                                                    
                    
                                                                                                         


                                









                                                                                               

                                              
                                                      
                         




                                                                            
                                                               
                                                                   
 

                                                                                            
                                 
                                                                      
                                                                                     

                                                                          
                                                                                       




                                                                    


                                 

                                                               
                               











































                                                                                      
         
                    

            



                                                                             
                                                         
 







                                                                                         
                 






                                                                                              
                      

                          



                                                                                                                                                          
 


                                                                                                               
 


                                                                                                 
                                         
                                                                                                               
                                                 





                                                                                                    
                                                 
                                                             
                                         
                                 
                                                           


                             
                         
                 

                                            


                                                                     







                                                                                                                                                  


                                                                                


                                                      
                                                                      

                                                        

                                            
                                                                                                                       



                                                         
                                                                                                              

                                                         
 
                                                                               
                                 
                                                              





                                                                                             

                                                         





                                                                                 




                                                                                                         

























                                                                                                            


                                 





                                                                              
         
                          
 





                                                                                                    
         

 


                                           
 
                           
 
                                    
                                    
 
                                           







                                 



                                           
                                



                                           
                                                                                           
 
                       




                       
                                                    
                
                                                   

         


                                                            
         

                                                                                      
                 
                                                      

                 
                                  
 
                                                                                                               

 




                                                      
 


                                                                                                         
 



                                      
                                                                                                                                                                 

                                  
                                    
 
                                                       



                                                                                                                               

                                                                                        
 
                                                                                     
                         

                                                                                  

                         

                                                                               
                                           

                                                                                
                                                  



                                                                                                                                
                                    
 


                                                                                                              
                         


                                                                                                          


                                                                    














                                                                                                  

                                                           
 

                                                                
                                      



                                                                        
                         

                                         



                                       

 

                                                               
                                                                                      


                                                

                                 
                                                   
                                                                            

                                                                                                                
                                                                                                        


                                        

                                                                                
                 


                                                           
                         












                                                                                                                                                                                   
                                 








                                                                                                                      
 


















                                                                                                               
 




                                                                                                       
 



                                                                                             
 


                                                                                          
 




                                                                                     


                            


                                                                                     



                         
                                                                                                               


                 

                                 


                                                                                               
/*
 # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
 #
 # This program is free software distributed under the GPL version 2.
 # See http://openslx.org/COPYING
 #
 # If you have any feedback please consult http://openslx.org/feedback and
 # send your suggestions, praise, or complaints to feedback@openslx.org
 #
 # General information about OpenSLX can be found at http://openslx.org/
 # --------------------------------------------------------------------------
 # x11FakeKeyboardHandler.cpp:
 #  - Handle keyboard events on X11 - interface
 # --------------------------------------------------------------------------
 */

#include <map>
#include <set>
#include <cassert>
// Qt headers need to be included before X11 headers
#include <QApplication>
#include <QtCore>
#include "x11FakeKeyboardHandler.h"
// #include <multimap>
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <X11/keysymdef.h>
#include <X11/extensions/XTest.h>
#ifdef HAVE_XINPUT2_H
# include <X11/extensions/XInput2.h>
#else
# include <X11/extensions/XInput.h>
#endif
#include <X11/XKBlib.h>
#include <src/util/consoleLogger.h>
#include "x11InputUtils.h"

// X11 supports 8 modifiers, so a bitmask of modifiers fits into a byte:
typedef unsigned char XModifiers;

// For debugging output, these are the names of modifier keys:
char modifiernames[][8] = {
		"SHIFT",
		"LOCK",
		"CONTROL",
		"MOD1",
		"MOD2",
		"MOD3",
		"MOD4",
		"MOD5"
};

// We generally want to find a key sequence with as few modifiers as
// possible. These are all modifier combinations sorted by number of
// bits set:
char modifierpriority[256] = {
		0, 1, 2, 4, 8, 16, 32, 64, 128,
		3, 5, 6, 9, 10, 12, 17, 18,
		20, 24, 33, 34, 36, 40, 48, 65,
		66, 68, 72, 80, 96, 129, 130, 132,
		136, 144, 160, 192, 7, 11, 13, 14,
		19, 21, 22, 25, 26, 28, 35, 37,
		38, 41, 42, 44, 49, 50, 52, 56,
		67, 69, 70, 73, 74, 76, 81, 82,
		84, 88, 97, 98, 100, 104, 112, 131,
		133, 134, 137, 138, 140, 145, 146, 148,
		152, 161, 162, 164, 168, 176, 193, 194,
		196, 200, 208, 224, 15, 23, 27, 29,
		30, 39, 43, 45, 46, 51, 53, 54,
		57, 58, 60, 71, 75, 77, 78, 83,
		85, 86, 89, 90, 92, 99, 101, 102,
		105, 106, 108, 113, 114, 116, 120, 135,
		139, 141, 142, 147, 149, 150, 153, 154,
		156, 163, 165, 166, 169, 170, 172, 177,
		178, 180, 184, 195, 197, 198, 201, 202,
		204, 209, 210, 212, 216, 225, 226, 228,
		232, 240, 31, 47, 55, 59, 61, 62,
		79, 87, 91, 93, 94, 103, 107, 109,
		110, 115, 117, 118, 121, 122, 124, 143,
		151, 155, 157, 158, 167, 171, 173, 174,
		179, 181, 182, 185, 186, 188, 199, 203,
		205, 206, 211, 213, 214, 217, 218, 220,
		227, 229, 230, 233, 234, 236, 241, 242,
		244, 248, 63, 95, 111, 119, 123, 125,
		126, 159, 175, 183, 187, 189, 190, 207,
		215, 219, 221, 222, 231, 235, 237, 238,
		243, 245, 246, 249, 250, 252, 127, 191,
		223, 239, 247, 251, 253, 254, 255
};

QString modifiersToString(XModifiers mods)
{
	QString s;
	for(int i = 0; i < 8; i++)
	{
		if(mods & (1<<i))
		{
			s += modifiernames[i];
			s += ", ";
		}
	}
	s.chop(2);
	return s;
}

/////////////////////////////////////////////////////////////////////////
// Input event translation code
/////////////////////////////////////////////////////////////////////////

// We need to translate between Qt's keycodes and X11's keycodes.
// Unfortunately, there does not seem to be a direct correspondence
// between them, so we are stuck with storing a lookup table:
typedef std::map<quint32, KeySym> KeycodeLookupTable;
KeycodeLookupTable keysyms;

// The following code has been partially autogenerated by
// an insane sed(1) script and then hand-edited to make
// the errors go away.
void initializeKeycodeLookupTable() {
	keysyms[Qt::Key_Escape] = XK_Escape;
	keysyms[Qt::Key_Tab] = XK_Tab;
	keysyms[Qt::Key_Backtab] = XK_ISO_Left_Tab;
	keysyms[Qt::Key_Backspace] = XK_BackSpace;
	keysyms[Qt::Key_Return] = XK_Return;
	keysyms[Qt::Key_Enter] = XK_KP_Enter;
	keysyms[Qt::Key_Insert] = XK_Insert;
	keysyms[Qt::Key_Delete] = XK_Delete;
	keysyms[Qt::Key_Pause] = XK_Pause;
	keysyms[Qt::Key_Print] = XK_Print;
	keysyms[Qt::Key_SysReq] = XK_Sys_Req;
	keysyms[Qt::Key_Clear] = XK_Clear;
	keysyms[Qt::Key_Home] = XK_Home;
	keysyms[Qt::Key_End] = XK_End;
	keysyms[Qt::Key_Left] = XK_Left;
	keysyms[Qt::Key_Up] = XK_Up;
	keysyms[Qt::Key_Right] = XK_Right;
	keysyms[Qt::Key_Down] = XK_Down;
	keysyms[Qt::Key_PageUp] = XK_Page_Up;
	keysyms[Qt::Key_PageDown] = XK_Page_Down;
	keysyms[Qt::Key_Shift] = XK_Shift_L;
	keysyms[Qt::Key_Control] = XK_Control_L;
	keysyms[Qt::Key_Meta] = XK_Meta_L;
	keysyms[Qt::Key_Alt] = XK_Alt_L;
	keysyms[Qt::Key_CapsLock] = XK_Caps_Lock;
	keysyms[Qt::Key_NumLock] = XK_Num_Lock;
	keysyms[Qt::Key_ScrollLock] = XK_Scroll_Lock;
	keysyms[Qt::Key_F1] = XK_F1;
	keysyms[Qt::Key_F2] = XK_F2;
	keysyms[Qt::Key_F3] = XK_F3;
	keysyms[Qt::Key_F4] = XK_F4;
	keysyms[Qt::Key_F5] = XK_F5;
	keysyms[Qt::Key_F6] = XK_F6;
	keysyms[Qt::Key_F7] = XK_F7;
	keysyms[Qt::Key_F8] = XK_F8;
	keysyms[Qt::Key_F9] = XK_F9;
	keysyms[Qt::Key_F10] = XK_F10;
	keysyms[Qt::Key_F11] = XK_F11;
	keysyms[Qt::Key_F12] = XK_F12;
	keysyms[Qt::Key_F13] = XK_F13;
	keysyms[Qt::Key_F14] = XK_F14;
	keysyms[Qt::Key_F15] = XK_F15;
	keysyms[Qt::Key_F16] = XK_F16;
	keysyms[Qt::Key_F17] = XK_F17;
	keysyms[Qt::Key_F18] = XK_F18;
	keysyms[Qt::Key_F19] = XK_F19;
	keysyms[Qt::Key_F20] = XK_F20;
	keysyms[Qt::Key_F21] = XK_F21;
	keysyms[Qt::Key_F22] = XK_F22;
	keysyms[Qt::Key_F23] = XK_F23;
	keysyms[Qt::Key_F24] = XK_F24;
	keysyms[Qt::Key_F25] = XK_F25;
	keysyms[Qt::Key_F26] = XK_F26;
	keysyms[Qt::Key_F27] = XK_F27;
	keysyms[Qt::Key_F28] = XK_F28;
	keysyms[Qt::Key_F29] = XK_F29;
	keysyms[Qt::Key_F30] = XK_F30;
	keysyms[Qt::Key_F31] = XK_F31;
	keysyms[Qt::Key_F32] = XK_F32;
	keysyms[Qt::Key_F33] = XK_F33;
	keysyms[Qt::Key_F34] = XK_F34;
	keysyms[Qt::Key_F35] = XK_F35;
	keysyms[Qt::Key_Super_L] = XK_Super_L;
	keysyms[Qt::Key_Super_R] = XK_Super_R;
	keysyms[Qt::Key_Menu] = XK_Menu;
	keysyms[Qt::Key_Hyper_L] = XK_Hyper_L;
	keysyms[Qt::Key_Hyper_R] = XK_Hyper_R;
	keysyms[Qt::Key_Help] = XK_Help;

	// Latin1 symbols do directly map to
	// Keycodes both in Qt and in X11:
	for(int i = 0x20; i < 0x100; i++)
	{
		keysyms[i] = i;
	}

	keysyms[Qt::Key_AltGr] = XK_ISO_Level3_Shift;
	keysyms[Qt::Key_Multi_key] = XK_Multi_key;
	keysyms[Qt::Key_Codeinput] = XK_Codeinput;
	keysyms[Qt::Key_SingleCandidate] = XK_SingleCandidate;
	keysyms[Qt::Key_MultipleCandidate] = XK_MultipleCandidate;
	keysyms[Qt::Key_PreviousCandidate] = XK_PreviousCandidate;
	keysyms[Qt::Key_Mode_switch] = XK_Mode_switch;
	keysyms[Qt::Key_Kanji] = XK_Kanji;
	keysyms[Qt::Key_Muhenkan] = XK_Muhenkan;
	keysyms[Qt::Key_Henkan] = XK_Henkan;
	keysyms[Qt::Key_Romaji] = XK_Romaji;
	keysyms[Qt::Key_Hiragana] = XK_Hiragana;
	keysyms[Qt::Key_Katakana] = XK_Katakana;
	keysyms[Qt::Key_Hiragana_Katakana] = XK_Hiragana_Katakana;
	keysyms[Qt::Key_Zenkaku] = XK_Zenkaku;
	keysyms[Qt::Key_Hankaku] = XK_Hankaku;
	keysyms[Qt::Key_Zenkaku_Hankaku] = XK_Zenkaku_Hankaku;
	keysyms[Qt::Key_Touroku] = XK_Touroku;
	keysyms[Qt::Key_Massyo] = XK_Massyo;
	keysyms[Qt::Key_Kana_Lock] = XK_Kana_Lock;
	keysyms[Qt::Key_Kana_Shift] = XK_Kana_Shift;
	keysyms[Qt::Key_Eisu_Shift] = XK_Eisu_Shift;
	keysyms[Qt::Key_Eisu_toggle] = XK_Eisu_toggle;
	keysyms[Qt::Key_Hangul] = XK_Hangul;
	keysyms[Qt::Key_Hangul_Hanja] = XK_Hangul_Hanja;
	keysyms[Qt::Key_Hangul] = XK_Hangul;
	keysyms[Qt::Key_Hangul_Start] = XK_Hangul_Start;
	keysyms[Qt::Key_Hangul_End] = XK_Hangul_End;
	keysyms[Qt::Key_Hangul_Hanja] = XK_Hangul_Hanja;
	keysyms[Qt::Key_Hangul_Jamo] = XK_Hangul_Jamo;
	keysyms[Qt::Key_Hangul_Romaja] = XK_Hangul_Romaja;
	keysyms[Qt::Key_Hangul_Jeonja] = XK_Hangul_Jeonja;
	keysyms[Qt::Key_Hangul_Banja] = XK_Hangul_Banja;
	keysyms[Qt::Key_Hangul_PreHanja] = XK_Hangul_PreHanja;
	keysyms[Qt::Key_Hangul_PostHanja] = XK_Hangul_PostHanja;
	keysyms[Qt::Key_Hangul_Special] = XK_Hangul_Special;
	keysyms[Qt::Key_Dead_Grave] = XK_dead_grave;
	keysyms[Qt::Key_Dead_Acute] = XK_dead_acute;
	keysyms[Qt::Key_Dead_Circumflex] = XK_dead_circumflex;
	keysyms[Qt::Key_Dead_Tilde] = XK_dead_tilde;
	keysyms[Qt::Key_Dead_Macron] = XK_dead_macron;
	keysyms[Qt::Key_Dead_Breve] = XK_dead_breve;
	keysyms[Qt::Key_Dead_Abovedot] = XK_dead_abovedot;
	keysyms[Qt::Key_Dead_Diaeresis] = XK_dead_diaeresis;
	keysyms[Qt::Key_Dead_Abovering] = XK_dead_abovering;
	keysyms[Qt::Key_Dead_Doubleacute] = XK_dead_doubleacute;
	keysyms[Qt::Key_Dead_Caron] = XK_dead_caron;
	keysyms[Qt::Key_Dead_Cedilla] = XK_dead_cedilla;
	keysyms[Qt::Key_Dead_Ogonek] = XK_dead_ogonek;
	keysyms[Qt::Key_Dead_Iota] = XK_dead_iota;
	keysyms[Qt::Key_Dead_Voiced_Sound] = XK_dead_voiced_sound;
	keysyms[Qt::Key_Dead_Semivoiced_Sound] = XK_dead_semivoiced_sound;
	keysyms[Qt::Key_Dead_Belowdot] = XK_dead_belowdot;
	keysyms[Qt::Key_Dead_Hook] = XK_dead_hook;
	keysyms[Qt::Key_Dead_Horn] = XK_dead_horn;
	keysyms[Qt::Key_Select] = XK_Select;
	keysyms[Qt::Key_Cancel] = XK_Cancel;
	keysyms[Qt::Key_Execute] = XK_Execute;
	keysyms[Qt::Key_unknown] = XK_VoidSymbol;
}

// We need to store how to generate KeySyms:
struct KeySymInfo
{
	KeyCode keycode;
	XModifiers neededModifiers;
	XModifiers modifierMask;
};
typedef std::map<KeySym, KeySymInfo> KeySymToInfoMap;
KeySymToInfoMap keysymInfos;

// What keys do we need to press to generate the
// modifiers?
int modifierKeycodes[8];
// How do the X11 modifiers relate to Qt's KeyboardModifiers?
Qt::KeyboardModifier modifierMeaning[8];

// How do the X11 modifiers correspond to Qt's KeyboardModifiers?
typedef std::map<XModifiers, Qt::KeyboardModifier> XToQtModifierMap;
XToQtModifierMap XToQtModifier;
typedef std::map<Qt::KeyboardModifier, XModifiers> QtToXModifierMap;
QtToXModifierMap QtToXModifier;


// And how do the modifiers relate to Keycodes?
typedef std::multimap<XModifiers, int> ModifierToKeycodeMap;
ModifierToKeycodeMap ModifierToKeycode;
typedef std::map<int, XModifiers> KeycodeToModifierMap;
KeycodeToModifierMap KeycodeToModifier;

#ifdef HAVE_XKBLIB_H
void recordModifierMapping(XkbDescPtr keybDesc, int keycode, Qt::KeyboardModifier qtMod)
{
	XModifiers rmod = 0;
	if(QtToXModifier.find(qtMod) == QtToXModifier.end())
	{
		for(int i = 1; i < 0x100; i <<= 1)
		{
			if(keybDesc->map->modmap[keycode] == i)
			{
				rmod = i;
				XToQtModifier[rmod] = qtMod;
				QtToXModifier[qtMod] = rmod;
			}
		}
	}
	else
	{
		rmod = QtToXModifier[qtMod];
	}

	if(rmod)
	{
		ModifierToKeycode.insert(std::make_pair(rmod, keycode));
		KeycodeToModifier[keycode] = rmod;
		ConsoleLog writeLine(QString("%1 %2 %3 ... generates modifier %4").arg(keycode, 3).arg("", 40).arg("", 40).arg(rmod, 2, 16));
	}
}
#endif

// We need to query the input devices, preferrable through XInput2, but
// if that is not available we will contend ourselves with XInput1:
#ifndef HAVE_XINPUT2_H
#	define XIAllDevices 1 /* does not matter */
#	define XIDeviceInfo XDeviceInfo
#	define XIQueryDevice(dpy, which, ninfos) XListInputDevices(dpy, ninfos)
#	define XIFreeDeviceInfo(infos) XFreeDeviceList(infos)
#	define XIMasterKeyboard IsXKeyboard
#	define XISlaveKeyboard IsXExtensionKeyboard
	static inline Atom* getDeviceProperties(Display* dpy, XIDeviceInfo* devinfo, int* nprops)
	{
		if(devinfo->use == IsXKeyboard)
		{
			// According to XOpenDevice(3X11) you cannot open the Core Keyboard.
			*nprops = 0;
			return 0;
		}

		XDevice* dev = XOpenDevice(dpy, devinfo->id);
		Atom* props = XListDeviceProperties(dpy, dev, nprops);
		XCloseDevice(dpy, dev);
		return props;
	}
#else
	static inline Atom* getDeviceProperties(Display* dpy, XIDeviceInfo* devinfo, int* nprops)
	{
		return XIListProperties(dpy, devinfo->deviceid, nprops);
	}
#endif

// Initialize the above data structures:
void initializeBasicKeycodes()
{
	// We temporarily need a list of all known KeySyms:
	typedef std::set<KeySym> KeySymSet;
	KeySymSet knownKeySyms;

	for(KeycodeLookupTable::const_iterator kcIter = keysyms.begin();
			kcIter != keysyms.end();
			kcIter++)
	{
		knownKeySyms.insert((*kcIter).second);
	}

	// Mark everything as unknown initially
	for(int i = 0; i < 8; i++) {
		modifierKeycodes[i] = -1;
	}
	keysymInfos.clear();

	Display* dpy = X11InputUtils::display();

	// Find out the valid range of keycodes
	int minKeycode, maxKeycode;
	XDisplayKeycodes(dpy, &minKeycode, &maxKeycode);

	// Initialize the XKB client-side code, and find out whether
	// the XKB extension is present in the server.
	int xkbOpcode, xkbEvent, xkbError, xkbMajor, xkbMinor;
#ifdef HAVE_XKBLIB_H
	bool xkbPresent = XkbQueryExtension(dpy, &xkbOpcode, &xkbEvent, &xkbError, &xkbMajor, &xkbMinor);
#else
	bool xkbPresent = false;
#endif

	if(!xkbPresent) {
		// No XKB. This is probably not a very recent
		// system, but we will do the best we can.
		// Retrieve the keyboard mapping
		int keysymsPerCode;
		const int count = maxKeycode - minKeycode + 1;
		KeySym* mapping = XGetKeyboardMapping(dpy, minKeycode, count, &keysymsPerCode);

		// and traverse every entry
		for(int i = 0; i < count; i++)
		{
			for(int j = 0; j < 0x100; j++)
			{
				const XModifiers mods = modifierpriority[j];
				if(mods >= keysymsPerCode)
					continue;

				const int idx = i * keysymsPerCode + mods;
				const KeySym ks = mapping[idx];
				const int keycode = minKeycode + i;

				// to find out if there is a KeySym there that we know about
				if(knownKeySyms.find(ks) != knownKeySyms.end())
				{
					// that we have not yet found,
					if(keysymInfos.find(ks) != keysymInfos.end())
						continue; // already found

					// and record its keycode and needed modifiers.
					KeySymInfo info;
					info.keycode = keycode;
					info.neededModifiers = mods;
					info.modifierMask = mods;
					keysymInfos[ks] = info;
				}
			}
		}

		// We are finished, free the mapping structure.
		XFree(mapping);

		// find out which keycodes cause the modifier state bits:
		XModifierKeymap* modkm;
		modkm = XGetModifierMapping(dpy);

		const int shiftCode = XKeysymToKeycode(dpy, XK_Shift_L);
		const int controlCode = XKeysymToKeycode(dpy, XK_Control_L);
		const int altCode = XKeysymToKeycode(dpy, XK_Alt_L);
		const int metaCode = XKeysymToKeycode(dpy, XK_Meta_L);
		const int switchCode = XKeysymToKeycode(dpy, XK_Mode_switch);

		// and use this information to find out which
		// X11 modifiers correspond to Qt's modifiers:
		for(int i = 0; i < 8; i++) {
			for(int j = 0; j < modkm->max_keypermod; j++) {
				const int idx = i * modkm->max_keypermod + j;
				const int kc = modkm->modifiermap[idx];

				ModifierToKeycode.insert(std::make_pair((1<<i), kc));
				KeycodeToModifier[kc] = (1<<i);
				if(kc == shiftCode) {
					XToQtModifier[1<<i] = Qt::ShiftModifier;
				} else if(kc == controlCode) {
					XToQtModifier[1<<i] = Qt::ControlModifier;
				} else if(kc == altCode) {
					XToQtModifier[1<<i] = Qt::AltModifier;
				} else if(kc == metaCode) {
					XToQtModifier[1<<i] = Qt::MetaModifier;
				} else if(kc == switchCode) {
					XToQtModifier[1<<i] = Qt::GroupSwitchModifier;
				}

				// and record the keycode we need to press/release
				// to activate or deactivate a modifier:
				if(modifierKeycodes[i] != -1) {
					continue; // already found
				}

				// select one arbitrarily
				modifierKeycodes[i] = kc;
			}
		}

		XFreeModifiermap(modkm);
	}
#ifdef HAVE_XKBLIB_H
	else
	{
		// XKB is present.

		// As different input devices can have different keymaps,
		// we need to find out what device XTest will send events on:
		unsigned int xkbDeviceId = XkbUseCoreKbd;


		// Is XInput present?
#if defined(HAVE_XINPUT_H) || defined(HAVE_XINPUT2_H)
		// We don't care about these:
		int i1, i2, i3;
		bool haveXInput = XQueryExtension(dpy, "XInputExtension", &i1, &i2, &i3);

		if(haveXInput)
		{
			Atom xtestDeviceProp = XInternAtom(dpy, "XTEST Device", false);

			// Find the list of input devices:
			int ndevinfos;
			XIDeviceInfo* devinfos = XIQueryDevice(dpy, XIAllDevices, &ndevinfos);
			if(devinfos)
			{
#ifndef HAVE_XINPUT2_H
#	define deviceid	id
#endif
				for(int i = 0; i < ndevinfos && xkbDeviceId == XkbUseCoreKbd; i++)
				{
					XIDeviceInfo* devinfo = devinfos + i;
					qDebug("Found device %lu of type %d with name %s", (unsigned long)devinfo->deviceid, devinfo->use, devinfo->name);

					// We want it to be a keyboard.
					if(devinfo->use != XIMasterKeyboard && devinfo->use != XISlaveKeyboard)
						continue;

					int nprops;
					Atom* props = getDeviceProperties(dpy, devinfo, &nprops);
					if(props)
					{
						for(int j = 0; j < nprops && xkbDeviceId == XkbUseCoreKbd; j++)
						{
							Atom prop = props[j];
							if(prop == xtestDeviceProp)
							{
								// The device is the XTest Keyboard:
								xkbDeviceId = devinfo->deviceid;
							}
						}
						XFree(props);
					}
				}
				XIFreeDeviceInfo(devinfos);
#ifdef deviceid /* XInput1 */
#	undef deviceid
#endif
			}
		}
#endif /* HAVE_XINPUT_H || HAVE_XINPUT2_H */

		// at this point, xkbDeviceId contains the identifier
		// of the XTEST Device, or xkbUseCoreKbd if none was
		// found.

		// Okay, we know which device to query. Now get its keymap:
		XkbDescPtr keybDesc = XkbGetKeyboard(dpy, XkbAllComponentsMask, xkbDeviceId);
		if(!keybDesc)
		{
			qWarning("Unable to retrieve keyboard description for device %d. Falling back to unreliable global mapping", xkbDeviceId);
		}

		// This is basically the same loop as above, it just queries XKB
		// instead of the old core Xlib routines.
		for(int i = minKeycode; i <= maxKeycode; i++)
		{
			for(int j = 0; j <= 0xff; j++)
			{
				XModifiers mods = modifierpriority[j];
				KeySym ks = 0;
				unsigned int unconsumed;
				if(keybDesc)
				{
					if(!XkbTranslateKeyCode(keybDesc, i, mods, &unconsumed, &ks) || ks == NoSymbol)
						continue;
				}
				else
				{
					if(!XkbLookupKeySym(dpy, i, mods, &unconsumed, &ks) || ks == NoSymbol)
						continue;
				}

				if(knownKeySyms.find(ks) != knownKeySyms.end())
				{
					if(mods & ~unconsumed)
					{
						// we would be recording extraneous modifiers
						continue;
					}

					if(keysymInfos.find(ks) != keysymInfos.end())
						continue;

					KeySymInfo info;
					info.keycode = i;
					info.neededModifiers = mods & unconsumed;
					info.modifierMask = unconsumed;
					keysymInfos[ks] = info;

					ConsoleLog writeLine(QString("%1 %2 %3 %4")
							.arg(i, 3)
							.arg(modifiersToString(info.neededModifiers), 40)
							.arg(modifiersToString(info.modifierMask), 40)
							.arg(XKeysymToString(ks)));
				}

				if(j == 0)
				{
					switch(ks)
					{
					case XK_Shift_L:
					case XK_Shift_R:
						recordModifierMapping(keybDesc, i, Qt::ShiftModifier);
						break;
					case XK_Control_L:
					case XK_Control_R:
						recordModifierMapping(keybDesc, i, Qt::ControlModifier);
						break;
					case XK_Meta_L:
					case XK_Meta_R:
						recordModifierMapping(keybDesc, i, Qt::MetaModifier);
						break;
					case XK_Alt_L:
					case XK_Alt_R:
						recordModifierMapping(keybDesc, i, Qt::AltModifier);
						break;
					case XK_ISO_Level3_Shift:
						recordModifierMapping(keybDesc, i, Qt::GroupSwitchModifier);
						break;
					}
				}
			}
		}

		// Free the keyboard description:
		if(keybDesc)
		{
			XkbFreeKeyboard(keybDesc, XkbAllComponentsMask, true);
		}
	}
#endif /* HAVE_XKBLIB_H */

	ConsoleLog writeLine("After initialization, XToQtModifier has the following entries:");
	for(XToQtModifierMap::const_iterator i = XToQtModifier.begin();
			i != XToQtModifier.end();
			i++)
	{
		ConsoleLog writeLine(QString("  %1 %2").arg(i->first, 2, 16).arg(i->second, 8, 16));
	}
}

// Translate a Qt KeyboardModifiers flag to
// its corresponding X11 modifier bitmask
XModifiers translateModifiers(quint32 mods)
{
	XModifiers ret = 0;

	for(int j = 0; j < 8; j++) {
		XModifiers i = 1<<j;

		if(mods & XToQtModifier[i])
		{
			ret |= i;
		}
	}

	return ret;
}

// We need to keep track of which modifiers
// are currently active
typedef std::set<int> IntSet;
IntSet pressedModifierKeys;
XModifiers currentModifiers = 0;

void trackModifiers(int keycode, bool down)
{
	// is this a modifier key?
	const bool isModifier = KeycodeToModifier.find(keycode) != KeycodeToModifier.end();

	if(!isModifier)
	{
		return;
	}

	if(down) {
		pressedModifierKeys.insert(keycode);
	} else {
		pressedModifierKeys.erase(keycode);
	}

	IntSet::iterator i, end = pressedModifierKeys.end();
	XModifiers modifs = 0;
	for(i = pressedModifierKeys.begin(); i != end; i++)
	{
		KeycodeToModifierMap::iterator foundCode = KeycodeToModifier.find(*i);
		if(foundCode != KeycodeToModifier.end())
		{
			modifs |= (*foundCode).second;
		}
	}
	currentModifiers = modifs;

	ConsoleLog writeLine(QString("[trackModifiers] current modifiers: %1").arg(modifiersToString(modifs)));
}

// And, if we need to tweak the modifiers to generate
// a particular keysym, we need to keep track of which
// ones we pressed or released.
typedef std::pair<int, bool> ModifierTweak;
typedef std::vector<ModifierTweak> TweakSequence;

// Tweak the modifiers to reach the neededState, from actualState
// and record tweaks in the tracker.
void tweakModifiers(Display* dpy, XModifiers neededState, XModifiers actualState, TweakSequence& tracker)
{
	if(neededState == actualState)
		// nothing to do.
		return;

	ConsoleLog writeLine(QString("tweakModifiers: Trying to get to `%1' from `%2'").arg(modifiersToString(neededState)).arg(modifiersToString(actualState)));
	for(int j = 0; j < 8; j++)
	{
		XModifiers i = 1<<j;

		// Do we need to activate the modifier?
		if((i & neededState) && !(i & actualState))
		{
			ConsoleLog writeLine(QString("tweakModifiers: Modifier %1 needs to be pressed").arg(modifiernames[j]));

			//find the keycode
			ModifierToKeycodeMap::iterator iter = ModifierToKeycode.find(i);

			if((iter == ModifierToKeycode.end()) || ((*iter).first != i))
			{
				// we don't know a key that triggers this modifier
				continue;
			}

			XTestFakeKeyEvent(dpy, (*iter).second, 1, CurrentTime);

			// record the tweak
			tracker.push_back(std::make_pair((*iter).second, true));
		}
		// or do we need to deactivate it?
		else if((!(i & neededState)) && (i & actualState))
		{
			ConsoleLog writeLine(QString("tweakModifiers: Modifier %1 needs to be released").arg(modifiernames[j]));

			int kc = -1;

			// first, check whether any of the currently pressed keys has triggered this modifier:
			IntSet::iterator iter, end = pressedModifierKeys.end();
			for(iter = pressedModifierKeys.begin(); iter != end; iter++)
			{
				KeycodeToModifierMap::iterator modmapIter = KeycodeToModifier.find(*iter);
				if(modmapIter != KeycodeToModifier.end()) {
					if(modmapIter->second == i) {
						kc = *iter;

						// release this key:
						XTestFakeKeyEvent(dpy, kc, 0, CurrentTime);
						tracker.push_back(std::make_pair(kc, false));
					}
				}
			}

			if(kc == -1) {
				// strange, but we need to release some other key:
				// we don't know which one, so we abort this and hope for the best
				continue;
			}
		}
	}
}

// Undo a recorded sequence of modifier tweaks
void untweakModifiers(Display* dpy, TweakSequence& tracker)
{
	TweakSequence::reverse_iterator i, end = tracker.rend();
	for(i = tracker.rbegin(); i != end; i++) {
		ModifierTweak& t = *i;
		XTestFakeKeyEvent(dpy, t.first, !t.second, CurrentTime);
	}
}

// initialize the handler
void X11FakeKeyboardHandler::initialize()
{
	initializeKeycodeLookupTable();
	initializeBasicKeycodes();
	pressedModifierKeys.clear();
	currentModifiers = 0;
}

// actually try our best to generate the correct input sequence
// for the event
void X11FakeKeyboardHandler::doHandle(InputEvent const& evt, InputEventContext const*)
{
	Display* dpy = X11InputUtils::display();

	XTestGrabControl(dpy, 1);

	// find out which keysym caused this event:
	KeycodeLookupTable::const_iterator i = keysyms.find(evt.qtKeysym());
	if(i == keysyms.end()) {
		// Special cases. We don't know how to directly translate those, so we will try to emulate them.
		ConsoleLog writeLine(QString("Unknown keysym received: %1").arg(evt.qtKeysym(), 8, 16));
	} else {
		KeySym ks = (*i).second;

		KeySymToInfoMap::const_iterator infoIter = keysymInfos.find(ks);
		if(infoIter != keysymInfos.end())
		{
			KeySymInfo info = infoIter->second;

			if(evt.isPress())
			{
				QString format = "Trying to press the key for %1 with modifiers %2 (X: %3, Qt: %4), while current modifiers are %5 and needed are %6 with mask %7";
				if(ks >= ' ' && ks < 0x100)
				{
					ConsoleLog writeLine(format
							.arg((char)ks)
							.arg(modifiersToString(translateModifiers(evt.qtModifiers())))
							.arg(translateModifiers(evt.qtModifiers()), 2, 16)
							.arg(evt.qtModifiers(), 8, 16)
							.arg(modifiersToString(currentModifiers))
							.arg(modifiersToString(info.neededModifiers))
							.arg(modifiersToString(info.modifierMask)));
				}
				else
				{
					ConsoleLog writeLine(format
							.arg(XKeysymToString(ks))
							.arg(modifiersToString(translateModifiers(evt.qtModifiers())))
							.arg(translateModifiers(evt.qtModifiers()), 2, 16)
							.arg(evt.qtModifiers(), 8, 16)
							.arg(modifiersToString(currentModifiers))
							.arg(modifiersToString(info.neededModifiers))
							.arg(modifiersToString(info.modifierMask)));
				}

				// what modifier keys do we need to press?
				XModifiers mods = translateModifiers(evt.qtModifiers());
				XModifiers needed = info.neededModifiers;
				XModifiers mask = info.modifierMask;

				// This is quite ad-hoc, but we do NOT want to
				// mask Alt.
				// On some configurations, the F-Keys consume Alt,
				// and when we AND it out, we disable Alt+Fn
				// combinations. That's just wrong.
				// FIXME: Are there situations
				// where we actually need to keep Alt masked?
				// Hint: I found none.
				// If so, how do we determine that?
				QtToXModifierMap::const_iterator altIter = QtToXModifier.find(Qt::AltModifier);
				if(altIter != QtToXModifier.end())
				{
					mask &= ~(altIter->second);
				}

				// Determine which modifiers actually need to be active
				XModifiers neededMods;
				neededMods = (mods
						& (needed | ~mask)) // Cleer the needed-clear modifiers
						| (needed & mask); // Set the needed-set modifiers

				// Modifiers need to be tracked BEFORE tweaking, as we may be
				// pressing a modifier key, and we don't want to release it
				// when untweaking.
				trackModifiers(info.keycode, true);

				// now, tweak the modifiers
				TweakSequence tweaks;
				tweakModifiers(dpy, neededMods, currentModifiers, tweaks);

				// press the key:
				XTestFakeKeyEvent(dpy, info.keycode, 1, CurrentTime);

				// and release the modifiers:
				untweakModifiers(dpy, tweaks);
			}
			else
			{
				// just release the key.
				XTestFakeKeyEvent(dpy, info.keycode, 0, CurrentTime);
				trackModifiers(info.keycode, false);
			}
		}
		else
		{
			ConsoleLog writeLine(QString("No keycode is mapped to `%1'").arg(XKeysymToString(ks)));
		}
	}

	XTestGrabControl(dpy, 0);

	// Since there may not be a mainloop running, we need to manually flush the event queue
	XFlush(dpy);
}