{"id":3073,"date":"2019-05-22T19:21:43","date_gmt":"2019-05-22T19:21:43","guid":{"rendered":"https:\/\/raspberry-projects.com\/pi\/?p=3073"},"modified":"2019-05-23T10:41:00","modified_gmt":"2019-05-23T10:41:00","slug":"reading-raw-keyboard-input","status":"publish","type":"post","link":"https:\/\/raspberry-projects.com\/pi\/programming-in-c\/keyboard-programming-in-c\/reading-raw-keyboard-input","title":{"rendered":"Reading Raw Keyboard Input"},"content":{"rendered":"\n<p>Functions like getchar() let you capture keyboard input, but in limited ways and without being able to detect various special keys such as ALT, CTRL, etc.  It also doesn&#8217;t allow you to detect Key Down and Key up events.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Using a GUI library<\/h4>\n\n\n\n<p>GUI libraries like GTK give you raw access to the keyboard keypresses and if you&#8217;re up for installing and configuring your project to use them then they&#8217;re a good solution.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Reading Keypresses Directly In Your C++ Application<\/h4>\n\n\n\n<p>There&#8217;s a nice simple way to read the raw keyboard events though and its demonstrated on the command line by the open source application evtest. You can install it using:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo apt-get install evtest<\/code><\/pre>\n\n\n\n<p>To use it you use this on the command line:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo evtest \/dev\/input\/event0<\/code><\/pre>\n\n\n\n<p>This will show your the raw events from the &#8220;event0&#8221; input.  There is a catch &#8211; event0 is just one of many possible input devices and the OS allocates the various input devices as it wishes. If you only have a keyboard connected to your RPi then its likely you only have event0 in the \/dev\/input\/ directory, but if you have other inputs or on other Linux systems you may have many different input devices.   Unfortunatly there isn&#8217;t a static give me the keyboard name that can be used (and indeed systems can have multiple keyboards of course), but there is a way to find the symlinks by looking in the directory \/dev\/input\/by-path (or \/dev\/input\/by-id).  These device paths will always map to whatever event# the OS has assigned to that input device.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Using the raw keypress functionality of evtest in your own application code<\/h4>\n\n\n\n<p>The evtest code can be viewed at:  <a href=\"http:\/\/elinux.org\/images\/9\/93\/Evtest.c\">http:\/\/elinux.org\/images\/9\/93\/Evtest.c<\/a><\/p>\n\n\n\n<p>The following example code is based on it and will detect every key down and key up event on the keyboard including the special keys such as ALT, SHIFT, CTRL, F1, etc, etc<\/p>\n\n\n\n<h5 class=\"wp-block-heading\">Using a loop<\/h5>\n\n\n\n<pre class=\"wp-block-code\"><code>#include &lt;linux\/input.h>\n#define BITS_PER_LONG (sizeof(long) * 8)\n#define NBITS(x) ((((x)-1)\/BITS_PER_LONG)+1)\n\nvoid KeyboardMonitor (void)\n{\n\tint FileDevice;\n\tint ReadDevice;\n\tint Index;\n\tstruct input_event InputEvent[64];\n\tint version;\n\tunsigned short id[4];\n\tunsigned long bit[EV_MAX][NBITS(KEY_MAX)];\n\n\t\/\/----- OPEN THE INPUT DEVICE -----\n\tif ((FileDevice = open(\"\/dev\/input\/event0\", O_RDONLY)) &lt; 0)\t\t\/\/&lt;&lt;&lt;&lt;SET THE INPUT DEVICE PATH HERE\n\t{\n\t\tperror(\"KeyboardMonitor can't open input device\");\n\t\tclose(FileDevice);\n\t\treturn;\n\t}\n\n\t\/\/----- GET DEVICE VERSION -----\n\tif (ioctl(FileDevice, EVIOCGVERSION, &amp;version))\n\t{\n\t\tperror(\"KeyboardMonitor can't get version\");\n\t\tclose(FileDevice);\n\t\treturn;\n\t}\n\t\/\/printf(\"Input driver version is %d.%d.%d\\n\", version >> 16, (version >> 8) &amp; 0xff, version &amp; 0xff);\n\n\t\/\/----- GET DEVICE INFO -----\n\tioctl(FileDevice, EVIOCGID, id);\n\t\/\/printf(\"Input device ID: bus 0x%x vendor 0x%x product 0x%x version 0x%x\\n\", id[ID_BUS], id[ID_VENDOR], id[ID_PRODUCT], id[ID_VERSION]);\n\t\n\tmemset(bit, 0, sizeof(bit));\n\tioctl(FileDevice, EVIOCGBIT(0, EV_MAX), bit[0]);\n\n\t\/\/----- READ KEYBOARD EVENTS -----\n\twhile (1)\n\t{\n\t\tReadDevice = read(FileDevice, InputEvent, sizeof(struct input_event) * 64);\n\n\t\tif (ReadDevice &lt; (int) sizeof(struct input_event))\n\t\t{\n\t\t\t\/\/This should never happen\n\t\t\t\/\/perror(\"KeyboardMonitor error reading - keyboard lost?\");\n\t\t\tclose(FileDevice);\n\t\t\treturn;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor (Index = 0; Index &lt; ReadDevice \/ sizeof(struct input_event); Index++)\n\t\t\t{\n\t\t\t\t\/\/We have:\n\t\t\t\t\/\/\tInputEvent[Index].time\t\ttimeval: 16 bytes (8 bytes for seconds, 8 bytes for microseconds)\n\t\t\t\t\/\/\tInputEvent[Index].type\t\tSee input-event-codes.h\n\t\t\t\t\/\/\tInputEvent[Index].code\t\tSee input-event-codes.h\n\t\t\t\t\/\/\tInputEvent[Index].value\t\t01 for keypress, 00 for release, 02 for autorepeat\n\t\t\t\t\n\t\t\t\tif (InputEvent[Index].type == EV_KEY)\n\t\t\t\t{\n\t\t\t\t\tif (InputEvent[Index].value == 2)\n\t\t\t\t\t{\n\t\t\t\t\t\t\/\/This is an auto repeat of a held down key\n\t\t\t\t\t\t\/\/cout &lt;&lt; (int)(InputEvent[Index].code) &lt;&lt; \" Auto Repeat\";\n\t\t\t\t\t\t\/\/cout &lt;&lt; endl;\n\t\t\t\t\t}\n\t\t\t\t\telse if (InputEvent[Index].value == 1)\n\t\t\t\t\t{\n\t\t\t\t\t\t\/\/----- KEY DOWN -----\n\t\t\t\t\t\tcout &lt;&lt; (int)(InputEvent[Index].code) &lt;&lt; \" Key Down\";\t\t\/\/input-event-codes.h\n\t\t\t\t\t\tcout &lt;&lt; endl;\n\t\t\t\t\t}\n\t\t\t\t\telse if (InputEvent[Index].value == 0)\n\t\t\t\t\t{\n\t\t\t\t\t\t\/\/----- KEY UP -----\n\t\t\t\t\t\tcout &lt;&lt; (int)(InputEvent[Index].code) &lt;&lt; \" Key Up\";\t\t\/\/input-event-codes.h\n\t\t\t\t\t\tcout &lt;&lt; endl;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t}\n}<\/code><\/pre>\n\n\n\n<h5 class=\"wp-block-heading\">Same code but implemented as a background thread<\/h5>\n\n\n\n<pre class=\"wp-block-code\"><code>\n#define\tKEYBOARD_MONITOR_INPUT_FIFO_NAME\t\t\"KeyboardMonitorInputFifo\"\nint our_input_fifo_filestream = -1;\n#include &lt;linux\/input.h>\n#define BITS_PER_LONG (sizeof(long) * 8)\n#define NBITS(x) ((((x)-1)\/BITS_PER_LONG)+1)\n\n\/\/*************************************************\n\/\/*************************************************\n\/\/********** KEYBOARD MONITOR INITIALISE **********\n\/\/*************************************************\n\/\/*************************************************\n\/\/Call once at startup\nvoid KeyboardMonitorInitialise (void)\n{\n\t\/\/--------------------------------------\n\t\/\/----- CREATE A FIFO \/ NAMED PIPE -----\n\t\/\/--------------------------------------\n\tint result;\n\n\tprintf(\"Making KeyboardMonitor FIFO...\\n\");\n\tresult = mkfifo(KEYBOARD_MONITOR_INPUT_FIFO_NAME, 0777);\t\t\/\/(This will fail if the fifo already exists in the system from the app previously running, this is fine)\n\tif (result == 0)\n\t{\n\t\t\/\/FIFO CREATED\n\t\tprintf(\"New FIFO created: %s\\n\", KEYBOARD_MONITOR_INPUT_FIFO_NAME);\n\t}\n\n\tprintf(\"Process %d opening FIFO %s\\n\", getpid(), KEYBOARD_MONITOR_INPUT_FIFO_NAME);\n\tour_input_fifo_filestream = open(KEYBOARD_MONITOR_INPUT_FIFO_NAME, (O_RDONLY | O_NONBLOCK));\n\t\t\t\t\t\/\/Possible flags:\n\t\t\t\t\t\/\/\tO_RDONLY - Open for reading only.\n\t\t\t\t\t\/\/\tO_WRONLY - Open for writing only.\n\t\t\t\t\t\/\/\tO_NDELAY \/ O_NONBLOCK (same function) - Enables nonblocking mode. When set read requests on the file can return immediately with a failure status\n\t\t\t\t\t\/\/\t\t\t\t\t\t\t\t\t\t\tif there is no input immediately available (instead of blocking). Likewise, write requests can also return\n\t\t\t\t\t\/\/\t\t\t\t\t\t\t\t\t\t\timmediately with a failure status if the output can't be written immediately.\n\tif (our_input_fifo_filestream != -1)\n\t\tprintf(\"Opened input FIFO: %i\\n\", our_input_fifo_filestream);\n\n\n\t\/\/-------------------------------------------------------\n\t\/\/----- RUN KEYBOARD CAPTURE ON A BACKGROUND THREAD -----\n\t\/\/-------------------------------------------------------\n\tint pid2 = fork();\n\tif(pid2 == 0)\n\t{\n\t\t\/\/----- THIS IS THE CHILD THREAD -----\n\t\tcout &lt;&lt; \"KeyboardMonitor child thread started\" &lt;&lt; endl;\n\n\t\tint FileDevice;\n\t\tint ReadDevice;\n\t\tint Index;\n\t\tstruct input_event InputEvent[64];\n\t\tint version;\n\t\tunsigned short id[4];\n\t\tunsigned long bit[EV_MAX][NBITS(KEY_MAX)];\n\t\tint NoError = 1;\n\t\tstringstream ss1;\n\n\t\t\/\/----- OPEN THE INPUT DEVICE -----\n\t\tif ((FileDevice = open(\"\/dev\/input\/event0\", O_RDONLY)) &lt; 0)\t\t\/\/&lt;&lt;&lt;&lt;SET THE INPUT DEVICE PATH HERE\n\t\t{\n\t\t\tperror(\"KeyboardMonitor can't open input device\");\n\t\t\tNoError = 0;\n\t\t}\n\n\t\t\/\/----- GET DEVICE VERSION -----\n\t\tif (ioctl(FileDevice, EVIOCGVERSION, &amp;version))\n\t\t{\n\t\t\tperror(\"KeyboardMonitor can't get version\");\n\t\t\tNoError = 0;\n\t\t}\n\t\t\/\/printf(\"Input driver version is %d.%d.%d\\n\", version >> 16, (version >> 8) &amp; 0xff, version &amp; 0xff);\n\n\t\t\/\/----- GET DEVICE INFO -----\n\t\tif (NoError)\n\t\t{\n\t\t\tioctl(FileDevice, EVIOCGID, id);\n\t\t\t\/\/printf(\"Input device ID: bus 0x%x vendor 0x%x product 0x%x version 0x%x\\n\", id[ID_BUS], id[ID_VENDOR], id[ID_PRODUCT], id[ID_VERSION]);\n\n\t\t\tmemset(bit, 0, sizeof(bit));\n\t\t\tioctl(FileDevice, EVIOCGBIT(0, EV_MAX), bit[0]);\n\t\t}\n\t\t\n\t\tcout &lt;&lt; \"KeyboardMonitor child thread running\" &lt;&lt; endl;\n\n\t\t\/\/Create an output filestream for this child thread\n\t\tint our_output_fifo_filestream = -1;\n\t\tour_output_fifo_filestream = open(KEYBOARD_MONITOR_INPUT_FIFO_NAME, (O_WRONLY | O_NONBLOCK));\n\t\t\t\t\t\t\t\/\/Possible flags:\n\t\t\t\t\t\t\t\/\/\tO_RDONLY - Open for reading only.\n\t\t\t\t\t\t\t\/\/\tO_WRONLY - Open for writing only.\n\t\t\t\t\t\t\t\/\/\tO_NDELAY \/ O_NONBLOCK (same function) - Enables nonblocking mode. When set read requests on the file can return immediately with a failure status\n\t\t\t\t\t\t\t\/\/\t\t\t\t\t\t\t\t\t\t\tif there is no input immediately available (instead of blocking). Likewise, write requests can also return\n\t\t\t\t\t\t\t\/\/\t\t\t\t\t\t\t\t\t\t\timmediately with a failure status if the output can't be written immediately.\n\t\tif (our_output_fifo_filestream != -1)\n\t\t\tcout &lt;&lt; \"Opened output FIFO: \" &lt;&lt; our_output_fifo_filestream &lt;&lt; endl;\n\t\t\n\t\t\/\/----- READ KEYBOARD EVENTS -----\n\t\twhile (NoError)\n\t\t{\n\t\t\tReadDevice = read(FileDevice, InputEvent, sizeof(struct input_event) * 64);\n\n\t\t\tif (ReadDevice &lt; (int) sizeof(struct input_event))\n\t\t\t{\n\t\t\t\t\/\/This should never happen\n\t\t\t\t\/\/perror(\"KeyboardMonitor error reading - keyboard lost?\");\n\t\t\t\tNoError = 0;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfor (Index = 0; Index &lt; ReadDevice \/ sizeof(struct input_event); Index++)\n\t\t\t\t{\n\t\t\t\t\t\/\/We have:\n\t\t\t\t\t\/\/\tInputEvent[Index].time\t\ttimeval: 16 bytes (8 bytes for seconds, 8 bytes for microseconds)\n\t\t\t\t\t\/\/\tInputEvent[Index].type\t\tSee input-event-codes.h\n\t\t\t\t\t\/\/\tInputEvent[Index].code\t\tSee input-event-codes.h\n\t\t\t\t\t\/\/\tInputEvent[Index].value\t\t01 for keypress, 00 for release, 02 for autorepeat\n\n\t\t\t\t\tif (InputEvent[Index].type == EV_KEY)\n\t\t\t\t\t{\n\t\t\t\t\t\tss1.clear();\n\t\t\t\t\t\tss1.str(std::string());\n\t\t\t\t\t\t\n\t\t\t\t\t\tif (InputEvent[Index].value == 2)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\/\/This is an auto repeat of a held down key\n\t\t\t\t\t\t\t\/\/ss1 &lt;&lt; (int)(InputEvent[Index].code) &lt;&lt; \" Auto Repeat\";\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (InputEvent[Index].value == 1)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\/\/----- KEY DOWN -----\n\t\t\t\t\t\t\tss1 &lt;&lt; (int)(InputEvent[Index].code) &lt;&lt; \" Key Down\";\t\/\/input-event-codes.h\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (InputEvent[Index].value == 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\/\/----- KEY UP -----\n\t\t\t\t\t\t\tss1 &lt;&lt; (int)(InputEvent[Index].code) &lt;&lt; \" Key Up\";\t\t\/\/input-event-codes.h\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\t\/\/----- WRITE EVENT TO THE OUTPUT FIFO -----\n\t\t\t\t\t\twrite(our_output_fifo_filestream, (void*)ss1.str().c_str(), strlen(ss1.str().c_str()));\n\t\t\t\t\t\t\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} \n\t\tclose(FileDevice);\n\t\tcout &lt;&lt; \"KeyboardMonitor child thread ending!\" &lt;&lt; endl;\n\t\t_exit(0);\t\t\/\/Don't forget to exit!\n\t}\n\n}\n\n\/\/**********************************************\n\/\/**********************************************\n\/\/********** KEYBOARD MONITOR PROCESS **********\n\/\/**********************************************\n\/\/**********************************************\n\/\/Call from main loop\nvoid KeyboardMonitorProcess (void)\n{\n\t\/\/---------------------------------------------\n\t\/\/----- CHECK FIFO FOR ANY RECEIVED BYTES -----\n\t\/\/---------------------------------------------\n\t\/\/ Read up to 255 characters from the port if they are there\n\tif (our_input_fifo_filestream != -1)\n\t{\n\t\tunsigned char InputBuffer[256];\n\t\tint BufferLength = read(our_input_fifo_filestream, (void*)InputBuffer, 255);\t\t\/\/Filestream, buffer to store in, number of bytes to read (max)\n\t\tif (BufferLength &lt; 0)\n\t\t{\n\t\t\t\/\/An error occured (this can happen)\n\t\t}\n\t\telse if (BufferLength == 0)\n\t\t{\n\t\t\t\/\/No data waiting\n\t\t}\n\t\telse\n\t\t{\n\t\t\t\/\/Bytes received\n\t\t\tInputBuffer[BufferLength] = '\\0';\n\t\t\tcout &lt;&lt; InputBuffer &lt;&lt; endl;\n\t\t}\n\t}\n}<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Functions like getchar() let you capture keyboard input, but in limited ways and without being able to detect various special keys such as ALT, CTRL, etc. It also doesn&#8217;t allow you to detect Key Down and Key up events. Using a GUI library GUI libraries like GTK give you raw access to the keyboard keypresses [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[154],"tags":[],"class_list":["post-3073","post","type-post","status-publish","format-standard","hentry","category-keyboard-programming-in-c"],"_links":{"self":[{"href":"https:\/\/raspberry-projects.com\/pi\/wp-json\/wp\/v2\/posts\/3073","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/raspberry-projects.com\/pi\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/raspberry-projects.com\/pi\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/raspberry-projects.com\/pi\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/raspberry-projects.com\/pi\/wp-json\/wp\/v2\/comments?post=3073"}],"version-history":[{"count":11,"href":"https:\/\/raspberry-projects.com\/pi\/wp-json\/wp\/v2\/posts\/3073\/revisions"}],"predecessor-version":[{"id":3087,"href":"https:\/\/raspberry-projects.com\/pi\/wp-json\/wp\/v2\/posts\/3073\/revisions\/3087"}],"wp:attachment":[{"href":"https:\/\/raspberry-projects.com\/pi\/wp-json\/wp\/v2\/media?parent=3073"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/raspberry-projects.com\/pi\/wp-json\/wp\/v2\/categories?post=3073"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/raspberry-projects.com\/pi\/wp-json\/wp\/v2\/tags?post=3073"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}