input: Add support for making libxkbcommon optional

In embedded environments, devices that appear as evdev "keyboards" often
have no resemblence to PC-style keyboards.  It is not uncommon for such
environments to have no concept of modifier keys and no need for XKB key
mapping; in these cases libxkbcommon initialization becomes unnecessary
startup overhead.  On some SOC platforms, xkb keymap compilation can
account for as much as 1/3 - 1/2 of the total compositor startup time.

This patch introduces a 'use_xkbcommon' flag in the core compositor
structure that indicates whether the compositor is running in "raw
keyboard" mode.  In raw keyboard mode, the compositor bypasses all
libxkbcommon initialization and processing.  'key' events containing the
integer keycode will continue to be delivered via the wl_keyboard
interface, but no 'keymap' event will be sent to clients.  No modifier
handling or keysym mapping is performed in this mode.

Note that upstream sample apps (e.g., weston-terminal or the
desktop-shell client) will not recognize raw keycodes and will not react
to keypresses when the compositor is operating in raw keyboard mode.
This is expected behavior; key events are still being sent to the
client, the client (and/or its toolkit) just isn't written to handle
keypresses without doing xkb keysym mapping.  Applications written
specifically for such embedded environments would be handling keypresses
via the raw keycode delivered as part of the 'key' event rather than
using xkb keysym mapping.

Whether to use xkbcommon is a global option that applies to all
compositor keyboard devices on the system; it is an all-or-nothing flag.
This patch simply adds conditional checks on whether xkbcommon is to be
used or not.

v3 don't send zero as the file descriptor - instead send the result of
opening /dev/null

v2 by Rob Bradford <rob@linux.intel.com>: the original version of the
patch used a "raw_keycodes" flag instead of the "use_xkbcommon" used in
this patch.

v1: Reviewed-by: Singh, Satyeshwar <satyeshwar.singh@intel.com>
v1: Reviewed-by: Bob Paauwe <bob.j.paauwe@intel.com>
diff --git a/src/input.c b/src/input.c
index 1737beb..a86bb7e 100644
--- a/src/input.c
+++ b/src/input.c
@@ -26,6 +26,7 @@
 #include <sys/mman.h>
 #include <assert.h>
 #include <unistd.h>
+#include <fcntl.h>
 
 #include "../shared/os-compatibility.h"
 #include "compositor.h"
@@ -817,6 +818,10 @@
 {
 	enum xkb_key_direction direction;
 
+	/* Keyboard modifiers don't exist in raw keyboard mode */
+	if (!seat->compositor->use_xkbcommon)
+		return;
+
 	if (state == WL_KEYBOARD_KEY_STATE_PRESSED)
 		direction = XKB_KEY_DOWN;
 	else
@@ -1206,9 +1211,17 @@
 	wl_list_insert(&seat->keyboard->resource_list, wl_resource_get_link(cr));
 	wl_resource_set_destructor(cr, unbind_resource);
 
-	wl_keyboard_send_keymap(cr, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
-				seat->xkb_info.keymap_fd,
-				seat->xkb_info.keymap_size);
+	if (seat->compositor->use_xkbcommon) {
+		wl_keyboard_send_keymap(cr, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
+					seat->xkb_info.keymap_fd,
+					seat->xkb_info.keymap_size);
+	} else {
+		int null_fd = open("/dev/null", O_RDONLY);
+		wl_keyboard_send_keymap(cr, WL_KEYBOARD_KEYMAP_FORMAT_NO_KEYMAP,
+					null_fd,
+					0);
+		close(null_fd);
+	}
 
 	if (seat->keyboard->focus &&
 	    wl_resource_get_client(seat->keyboard->focus->resource) == client) {
@@ -1267,6 +1280,13 @@
 weston_compositor_xkb_init(struct weston_compositor *ec,
 			   struct xkb_rule_names *names)
 {
+	/*
+	 * If we're operating in raw keyboard mode, libxkbcommon isn't used and
+	 * shouldn't be initialized.
+	 */
+	if (!ec->use_xkbcommon)
+		return 0;
+
 	if (ec->xkb_context == NULL) {
 		ec->xkb_context = xkb_context_new(0);
 		if (ec->xkb_context == NULL) {
@@ -1301,6 +1321,13 @@
 void
 weston_compositor_xkb_destroy(struct weston_compositor *ec)
 {
+	/*
+	 * If we're operating in raw keyboard mode, we never initialized
+	 * libxkbcommon so there's no cleanup to do either.
+	 */
+	if (!ec->use_xkbcommon)
+		return;
+
 	free((char *) ec->xkb_names.rules);
 	free((char *) ec->xkb_names.model);
 	free((char *) ec->xkb_names.layout);
@@ -1405,24 +1432,27 @@
 	if (seat->keyboard)
 		return 0;
 
-	if (keymap != NULL) {
-		seat->xkb_info.keymap = xkb_map_ref(keymap);
-		if (weston_xkb_info_new_keymap(&seat->xkb_info) < 0)
-			return -1;
-	} else {
-		if (weston_compositor_build_global_keymap(seat->compositor) < 0)
-			return -1;
-		seat->xkb_info = seat->compositor->xkb_info;
-		seat->xkb_info.keymap = xkb_map_ref(seat->xkb_info.keymap);
-	}
 
-	seat->xkb_state.state = xkb_state_new(seat->xkb_info.keymap);
-	if (seat->xkb_state.state == NULL) {
-		weston_log("failed to initialise XKB state\n");
-		return -1;
-	}
+	if (seat->compositor->use_xkbcommon) {
+		if (keymap != NULL) {
+			seat->xkb_info.keymap = xkb_map_ref(keymap);
+			if (weston_xkb_info_new_keymap(&seat->xkb_info) < 0)
+				return -1;
+		} else {
+			if (weston_compositor_build_global_keymap(seat->compositor) < 0)
+				return -1;
+			seat->xkb_info = seat->compositor->xkb_info;
+			seat->xkb_info.keymap = xkb_map_ref(seat->xkb_info.keymap);
+		}
 
-	seat->xkb_state.leds = 0;
+		seat->xkb_state.state = xkb_state_new(seat->xkb_info.keymap);
+		if (seat->xkb_state.state == NULL) {
+			weston_log("failed to initialise XKB state\n");
+			return -1;
+		}
+
+		seat->xkb_state.leds = 0;
+	}
 
 	keyboard = weston_keyboard_create();
 	if (keyboard == NULL) {
@@ -1507,9 +1537,11 @@
 	wl_list_remove(&seat->link);
 	/* The global object is destroyed at wl_display_destroy() time. */
 
-	if (seat->xkb_state.state != NULL)
-		xkb_state_unref(seat->xkb_state.state);
-	xkb_info_destroy(&seat->xkb_info);
+	if (seat->compositor->use_xkbcommon) {
+		if (seat->xkb_state.state != NULL)
+			xkb_state_unref(seat->xkb_state.state);
+		xkb_info_destroy(&seat->xkb_info);
+	}
 
 	if (seat->pointer)
 		weston_pointer_destroy(seat->pointer);