Browse Source

Fixed to remove usage of XKeycodeToKeysym which is deprecated and should not be used. This version provides support for X11R5 and newer via XGetKeyboardMapping and for modern systems uses more efficient XkbKeycodeToKeysym if supported at compile time and available at runtime in the X server used for display. This works in both physically connected frame buffers as well as virtual frame buffers such as VNC proxies.

David Wimsey 11 years ago
parent
commit
423e6aaf23

+ 6 - 0
Build/CMakeLists.txt

@@ -240,6 +240,12 @@ if(BUILD_SAMPLES)
     	find_package(X11 REQUIRED)
         if (X11_FOUND)
         	list(APPEND sample_LIBRARIES ${X11_LIBRARIES})
+		# shell/src/x11/InputX11.cpp:InitialiseX11Keymap uses Xkb if
+		# possible instead of XGetKeyboardMapping for performance
+		if(X11_Xkb_FOUND)
+			FIND_PACKAGE_MESSAGE(X11 "Found X11 KBlib: ${X11_X11_LIB}" "[${X11_X11_LIB}][${X11_XkbINCLUDE_DIR}]")
+			add_definitions(-DHAS_X11XKBLIB)
+		endif()
         endif()
     endif()
    

+ 5 - 0
Samples/shell/include/x11/InputX11.h

@@ -47,6 +47,11 @@ public:
 
 	/// Process the windows message
 	static void ProcessXEvent(Display* display, const XEvent& event);
+
+	// Initialises Xkb extension if available or reads keymap from X11
+	// server otherwise.  This is internal to the X11 subsystem and
+	// has nothing to do with libRocket's mapping.
+	static void InitialiseX11Keymap(Display *display);
 };
 
 #endif

+ 77 - 8
Samples/shell/src/x11/InputX11.cpp

@@ -31,6 +31,9 @@
 #include <Rocket/Debugger.h>
 #include <Shell.h>
 #include <X11/Xlib.h>
+#ifdef HAS_X11XKBLIB
+#include <X11/XKBlib.h>
+#endif // HAS_X11XKBLIB
 #include <X11/keysym.h>
 #include <X11/Xutil.h>
 
@@ -40,6 +43,13 @@ static int GetKeyModifierState(int x_state);
 static const int KEYMAP_SIZE = 256;
 static Rocket::Core::Input::KeyIdentifier key_identifier_map[KEYMAP_SIZE];
 
+#ifdef HAS_X11XKBLIB
+static bool has_xkblib = false;
+#endif // HAS_X11XKBLIB
+
+static int min_keycode, max_keycode, keysyms_per_keycode;
+static KeySym *x11_key_mapping = NULL;
+
 bool InputX11::Initialise()
 {
 	InitialiseKeymap();
@@ -50,6 +60,35 @@ void InputX11::Shutdown()
 {
 }
 
+void InputX11::InitialiseX11Keymap(Display *display)
+{
+	ROCKET_ASSERT(display != NULL);
+
+#ifdef HAS_X11XKBLIB
+	int opcode_rtrn = -1;
+	int event_rtrn = -1;
+	int error_rtrn = -1;
+	int major_in_out = -1;
+	int minor_in_out = -1;
+
+	// Xkb extension may not exist in the server.  This checks for its
+	// existence and initializes the extension if available.
+	has_xkblib = XkbQueryExtension(display, &opcode_rtrn, &event_rtrn, &error_rtrn, &major_in_out, &minor_in_out);
+
+	// if Xkb isn't available, fall back to using XGetKeyboardMapping, 
+	// which may occur if libRocket is compiled with Xkb support but the
+	// server doesn't support it.  This occurs with older X11 servers or
+	// virtual framebuffers such as x11vnc server.
+	if(!has_xkblib)
+#endif // HAS_X11XKBLIB
+	{
+		XDisplayKeycodes(display, &min_keycode, &max_keycode);
+
+		ROCKET_ASSERT(x11_key_mapping != NULL);
+		x11_key_mapping = XGetKeyboardMapping(display, min_keycode, max_keycode + 1 - min_keycode, &keysyms_per_keycode);
+	}
+}
+
 void InputX11::ProcessXEvent(Display* display, const XEvent& event)
 {
 	// Process all mouse and keyboard events
@@ -95,11 +134,26 @@ void InputX11::ProcessXEvent(Display* display, const XEvent& event)
 
 		case KeyPress: 
 		{
-			KeySym sym = XKeycodeToKeysym(display, event.xkey.keycode, 0);
-			KeySym lower_sym, upper_sym;
-			XConvertCase(sym, &lower_sym, &upper_sym);
+			int group_index = 0; // this is always 0 for our limited example
+			Rocket::Core::Input::KeyIdentifier key_identifier;
+#ifdef HAS_X11XKBLIB
+			if(has_xkblib)
+			{
+				KeySym sym = XkbKeycodeToKeysym(display, event.xkey.keycode, 0, group_index);
+
+				key_identifier = key_identifier_map[sym & 0xFF];
+			}
+			else
+#endif // HAS_X11XKBLIB
+			{
+				KeySym sym = x11_key_mapping[(event.xkey.keycode - min_keycode) * keysyms_per_keycode + group_index];
+
+				KeySym lower_sym, upper_sym;
+				XConvertCase(sym, &lower_sym, &upper_sym);
+
+				key_identifier = key_identifier_map[lower_sym & 0xFF];
+			}
 
-			Rocket::Core::Input::KeyIdentifier key_identifier = key_identifier_map[lower_sym & 0xFF];
 			int key_modifier_state = GetKeyModifierState(event.xkey.state);
 
 			// Check for a shift-~ to toggle the debugger.
@@ -121,11 +175,26 @@ void InputX11::ProcessXEvent(Display* display, const XEvent& event)
 
 		case KeyRelease:
 		{
-			KeySym sym = XKeycodeToKeysym(display, event.xkey.keycode, 0);
-			KeySym lower_sym, upper_sym;
-			XConvertCase(sym, &lower_sym, &upper_sym);
+			int group_index = 0; // this is always 0 for our limited example
+			Rocket::Core::Input::KeyIdentifier key_identifier;
+#ifdef HAS_X11XKBLIB
+			if(has_xkblib)
+			{
+				KeySym sym = XkbKeycodeToKeysym(display, event.xkey.keycode, 0, group_index);
+
+				key_identifier = key_identifier_map[sym & 0xFF];
+			}
+			else
+#endif // HAS_X11XKBLIB
+			{
+				KeySym sym = x11_key_mapping[(event.xkey.keycode - min_keycode) * keysyms_per_keycode + group_index];
+
+				KeySym lower_sym, upper_sym;
+				XConvertCase(sym, &lower_sym, &upper_sym);
+
+				key_identifier = key_identifier_map[lower_sym & 0xFF];
+			}
 
-			Rocket::Core::Input::KeyIdentifier key_identifier = key_identifier_map[lower_sym & 0xFF];
 			int key_modifier_state = GetKeyModifierState(event.xkey.state);
 			if (key_identifier != Rocket::Core::Input::KI_UNKNOWN)
 				context->ProcessKeyUp(key_identifier, key_modifier_state);

+ 5 - 0
Samples/shell/src/x11/ShellX11.cpp

@@ -74,6 +74,11 @@ bool Shell::OpenWindow(const char* name, bool attach_opengl)
 	if (display == NULL)
 		return false;
 
+	// This initialise they keyboard to keycode mapping system of X11
+	// itself.  It must be done here as it needs to query the connected
+	// X server display for information about its install keymap abilities.
+	InputX11::InitialiseX11Keymap(display);
+
 	screen = XDefaultScreen(display);
 
 	// Fetch an appropriate 32-bit visual interface.