summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRalph Nathan <ralphnathan@google.com>2015-12-02 20:42:42 -0800
committerRalph Nathan <ralphnathan@google.com>2015-12-03 11:53:17 -0800
commit9a13a5ea03f17f73f556f3ed13c9469e60aa9c46 (patch)
treecdaef8d5d7d1ed1cd65218922699c919112b05bc
parent9f9e75e8fefa0a30fa22ec2011ed41d6e5eb8cc1 (diff)
downloadcommon-9a13a5ea03f17f73f556f3ed13c9469e60aa9c46.tar.gz
Move keyboard example from device/generic/brillo.
Centralize all the example code for Brillo by moving the keyboard example from device/generic/brillo. BUG=25927954 TEST=keyboard-example builds for dragonboard. Change-Id: I58d43002df25864b49ade51d1c5dfcdceaaac3c0
-rw-r--r--keyboard_example/Android.mk24
-rw-r--r--keyboard_example/README14
-rw-r--r--keyboard_example/keyboard_example.cpp185
3 files changed, 223 insertions, 0 deletions
diff --git a/keyboard_example/Android.mk b/keyboard_example/Android.mk
new file mode 100644
index 0000000..f2c5942
--- /dev/null
+++ b/keyboard_example/Android.mk
@@ -0,0 +1,24 @@
+#
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := keyboard-example
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := keyboard_example.cpp
+include $(BUILD_EXECUTABLE)
diff --git a/keyboard_example/README b/keyboard_example/README
new file mode 100644
index 0000000..7ecd119
--- /dev/null
+++ b/keyboard_example/README
@@ -0,0 +1,14 @@
+This folder contains example code for writing native apps that use keyboard
+inputs.
+
+Example file:
+ keyboard_example.cpp:
+ Demos listing devices and querying their metadata, polling for keyboard
+ events and detecting different events (pressed, released, hold) for a given
+ key.
+
+ This uses the evdev kernel mechanism to handle input events. It does not use
+ the input HAL.
+ See https://www.kernel.org/doc/Documentation/input/input.txt and
+ https://www.kernel.org/doc/Documentation/input/event-codes.txt for more
+ information.
diff --git a/keyboard_example/keyboard_example.cpp b/keyboard_example/keyboard_example.cpp
new file mode 100644
index 0000000..3358be9
--- /dev/null
+++ b/keyboard_example/keyboard_example.cpp
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/input.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/poll.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+using std::string;
+using std::vector;
+
+static const int kMaxDeviceName = 80;
+
+// Reads the list of files in |directory_path| into |filenames|.
+void ListDirectory(const string& directory_path, vector<string>* filenames) {
+ if (!filenames) {
+ printf("Error: filenames is null.\n");
+ exit(1);
+ }
+ DIR* directory = opendir(directory_path.c_str());
+ if (!directory) {
+ printf("Failed to open %s.\n", directory_path.c_str());
+ return;
+ }
+ struct dirent *entry = NULL;
+ while ((entry = readdir(directory))) {
+ if (strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..")) {
+ filenames->push_back(directory_path + "/" + entry->d_name);
+ }
+ }
+}
+
+// Returns true iff the device reports EV_KEY events.
+bool HasKeyEvents(int device_fd) {
+ unsigned long evbit = 0;
+ // Get the bit field of available event types.
+ ioctl(device_fd, EVIOCGBIT(0, sizeof(evbit)), &evbit);
+ return evbit & (1 << EV_KEY);
+}
+
+// Returns true iff the given device has |key|.
+bool HasSpecificKey(int device_fd, unsigned int key) {
+ size_t nchar = KEY_MAX/8 + 1;
+ unsigned char bits[nchar];
+ // Get the bit fields of available keys.
+ ioctl(device_fd, EVIOCGBIT(EV_KEY, sizeof(bits)), &bits);
+ return bits[key/8] & (1 << (key % 8));
+}
+
+int main(int argc, char** argv) {
+ string input_directory = "/dev/input";
+ if (argc < 2) {
+ printf("No input directory specified, using the default one: %s.\n",
+ input_directory.c_str());
+ } else {
+ input_directory = argv[1];
+ }
+
+ vector<string> filenames;
+ ListDirectory(input_directory, &filenames);
+ printf("%i devices found in %s.\n", filenames.size(),
+ input_directory.c_str());
+
+ // Initializing the pollfd structures.
+ vector<pollfd> poll_fds;
+ char device_name[kMaxDeviceName];
+ for (size_t i = 0; i < filenames.size(); i++) {
+ int fd = open(filenames[i].c_str(), O_RDONLY);
+ if (fd < 0) {
+ printf("Failed to open %s for reading: %s\n", filenames[i].c_str(),
+ strerror(errno));
+ return 1;
+ }
+
+ // Reads the device name.
+ if(ioctl(fd, EVIOCGNAME(sizeof(device_name) - 1),
+ &device_name) < 1) {
+ device_name[0] = '\0';
+ }
+
+ if (!HasKeyEvents(fd)) {
+ printf("Discarding %s (%s): does not report any EV_KEY events.\n",
+ filenames[i].c_str(), device_name);
+ continue;
+ }
+ if (!HasSpecificKey(fd, KEY_B)) {
+ printf("Discarding %s (%s): does not have a B key.\n",
+ filenames[i].c_str(), device_name);
+ continue;
+ }
+ poll_fds.push_back(pollfd{fd, POLLIN, 0});
+
+ printf("Adding device %s: %s\n", filenames[i].c_str(), device_name);
+ }
+
+ if (poll_fds.empty()) {
+ printf("No keyboard detected, exiting.\n");
+ return 1;
+ }
+
+ // Number of repeated events + 1.
+ int count = 1;
+ // Events accumulated for each device.
+ vector<vector<input_event>> events(poll_fds.size());
+
+ const char instructions[] = "Press B to start the game and release B to go "
+ "back to the main menu.";
+ printf("%s\n", instructions);
+ while (true) {
+ // Wait for data to be available on one of the file descriptors without
+ // timeout (-1).
+ poll(poll_fds.data(), poll_fds.size(), -1);
+
+ for (size_t i = 0; i < poll_fds.size(); i++) {
+ if (poll_fds[i].revents & POLLIN) {
+ struct input_event event;
+ if (read(poll_fds[i].fd, &event, sizeof(event)) != sizeof(event)) {
+ printf("Failed to read an event.\n");
+ return 1;
+ }
+
+ // When receiving a key event for B, add it to the list of events.
+ // Don't process it yet as there might be other events in that report.
+ if (event.type == EV_KEY && event.code == KEY_B) {
+ events[i].push_back(event);
+ }
+
+ // A SYN_REPORT event signals the end of a report, process all the
+ // previously accumulated events.
+ // At that point we only have events for B in the event vector.
+ if (event.type == EV_SYN && event.code == SYN_REPORT) {
+ for (vector<input_event>::iterator it = events[i].begin();
+ it != events[i].end(); it++) {
+ switch (it->value) {
+ case 0: {
+ // A value of 0 indicates a "key released" event.
+ double percent = 1. - 1./count;
+ printf("%.02f%% loaded.\n", percent * 100);
+ printf("B released, returning to the main menu.\n\n");
+ printf("%s\n", instructions);
+ break;
+ }
+ case 1: {
+ // A value of 1 indicates a "key pressed" event.
+ printf("B pressed, starting the game...\n");
+ count = 1;
+ break;
+ }
+ case 2: {
+ // A value of 2 indicates a "key repeat" event.
+ count++;
+ break;
+ }
+ default: {}
+ }
+ }
+ // Discard all processed events.
+ events[i].clear();
+ }
+ }
+ }
+ }
+ return 0;
+}
+