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’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 and if you’re up for installing and configuring your project to use them then they’re a good solution.

Reading Keypresses Directly In Your C++ Application

There’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:

sudo apt-get install evtest

To use it you use this on the command line:

sudo evtest /dev/input/event0

This will show your the raw events from the “event0” input. There is a catch – 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’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.

Using the raw keypress functionality of evtest in your own application code

The evtest code can be viewed at: http://elinux.org/images/9/93/Evtest.c

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

Using a loop
#include <linux/input.h>
#define BITS_PER_LONG (sizeof(long) * 8)
#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1)

void KeyboardMonitor (void)
{
	int FileDevice;
	int ReadDevice;
	int Index;
	struct input_event InputEvent[64];
	int version;
	unsigned short id[4];
	unsigned long bit[EV_MAX][NBITS(KEY_MAX)];

	//----- OPEN THE INPUT DEVICE -----
	if ((FileDevice = open("/dev/input/event0", O_RDONLY)) < 0)		//<<<<SET THE INPUT DEVICE PATH HERE
	{
		perror("KeyboardMonitor can't open input device");
		close(FileDevice);
		return;
	}

	//----- GET DEVICE VERSION -----
	if (ioctl(FileDevice, EVIOCGVERSION, &version))
	{
		perror("KeyboardMonitor can't get version");
		close(FileDevice);
		return;
	}
	//printf("Input driver version is %d.%d.%d\n", version >> 16, (version >> 8) & 0xff, version & 0xff);

	//----- GET DEVICE INFO -----
	ioctl(FileDevice, EVIOCGID, id);
	//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]);
	
	memset(bit, 0, sizeof(bit));
	ioctl(FileDevice, EVIOCGBIT(0, EV_MAX), bit[0]);

	//----- READ KEYBOARD EVENTS -----
	while (1)
	{
		ReadDevice = read(FileDevice, InputEvent, sizeof(struct input_event) * 64);

		if (ReadDevice < (int) sizeof(struct input_event))
		{
			//This should never happen
			//perror("KeyboardMonitor error reading - keyboard lost?");
			close(FileDevice);
			return;
		}
		else
		{
			for (Index = 0; Index < ReadDevice / sizeof(struct input_event); Index++)
			{
				//We have:
				//	InputEvent[Index].time		timeval: 16 bytes (8 bytes for seconds, 8 bytes for microseconds)
				//	InputEvent[Index].type		See input-event-codes.h
				//	InputEvent[Index].code		See input-event-codes.h
				//	InputEvent[Index].value		01 for keypress, 00 for release, 02 for autorepeat
				
				if (InputEvent[Index].type == EV_KEY)
				{
					if (InputEvent[Index].value == 2)
					{
						//This is an auto repeat of a held down key
						//cout << (int)(InputEvent[Index].code) << " Auto Repeat";
						//cout << endl;
					}
					else if (InputEvent[Index].value == 1)
					{
						//----- KEY DOWN -----
						cout << (int)(InputEvent[Index].code) << " Key Down";		//input-event-codes.h
						cout << endl;
					}
					else if (InputEvent[Index].value == 0)
					{
						//----- KEY UP -----
						cout << (int)(InputEvent[Index].code) << " Key Up";		//input-event-codes.h
						cout << endl;
					}
				}
			}
		}

	}
}
Same code but implemented as a background thread

#define	KEYBOARD_MONITOR_INPUT_FIFO_NAME		"KeyboardMonitorInputFifo"
int our_input_fifo_filestream = -1;
#include <linux/input.h>
#define BITS_PER_LONG (sizeof(long) * 8)
#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1)

//*************************************************
//*************************************************
//********** KEYBOARD MONITOR INITIALISE **********
//*************************************************
//*************************************************
//Call once at startup
void KeyboardMonitorInitialise (void)
{
	//--------------------------------------
	//----- CREATE A FIFO / NAMED PIPE -----
	//--------------------------------------
	int result;

	printf("Making KeyboardMonitor FIFO...\n");
	result = mkfifo(KEYBOARD_MONITOR_INPUT_FIFO_NAME, 0777);		//(This will fail if the fifo already exists in the system from the app previously running, this is fine)
	if (result == 0)
	{
		//FIFO CREATED
		printf("New FIFO created: %s\n", KEYBOARD_MONITOR_INPUT_FIFO_NAME);
	}

	printf("Process %d opening FIFO %s\n", getpid(), KEYBOARD_MONITOR_INPUT_FIFO_NAME);
	our_input_fifo_filestream = open(KEYBOARD_MONITOR_INPUT_FIFO_NAME, (O_RDONLY | O_NONBLOCK));
					//Possible flags:
					//	O_RDONLY - Open for reading only.
					//	O_WRONLY - Open for writing only.
					//	O_NDELAY / O_NONBLOCK (same function) - Enables nonblocking mode. When set read requests on the file can return immediately with a failure status
					//											if there is no input immediately available (instead of blocking). Likewise, write requests can also return
					//											immediately with a failure status if the output can't be written immediately.
	if (our_input_fifo_filestream != -1)
		printf("Opened input FIFO: %i\n", our_input_fifo_filestream);


	//-------------------------------------------------------
	//----- RUN KEYBOARD CAPTURE ON A BACKGROUND THREAD -----
	//-------------------------------------------------------
	int pid2 = fork();
	if(pid2 == 0)
	{
		//----- THIS IS THE CHILD THREAD -----
		cout << "KeyboardMonitor child thread started" << endl;

		int FileDevice;
		int ReadDevice;
		int Index;
		struct input_event InputEvent[64];
		int version;
		unsigned short id[4];
		unsigned long bit[EV_MAX][NBITS(KEY_MAX)];
		int NoError = 1;
		stringstream ss1;

		//----- OPEN THE INPUT DEVICE -----
		if ((FileDevice = open("/dev/input/event0", O_RDONLY)) < 0)		//<<<<SET THE INPUT DEVICE PATH HERE
		{
			perror("KeyboardMonitor can't open input device");
			NoError = 0;
		}

		//----- GET DEVICE VERSION -----
		if (ioctl(FileDevice, EVIOCGVERSION, &version))
		{
			perror("KeyboardMonitor can't get version");
			NoError = 0;
		}
		//printf("Input driver version is %d.%d.%d\n", version >> 16, (version >> 8) & 0xff, version & 0xff);

		//----- GET DEVICE INFO -----
		if (NoError)
		{
			ioctl(FileDevice, EVIOCGID, id);
			//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]);

			memset(bit, 0, sizeof(bit));
			ioctl(FileDevice, EVIOCGBIT(0, EV_MAX), bit[0]);
		}
		
		cout << "KeyboardMonitor child thread running" << endl;

		//Create an output filestream for this child thread
		int our_output_fifo_filestream = -1;
		our_output_fifo_filestream = open(KEYBOARD_MONITOR_INPUT_FIFO_NAME, (O_WRONLY | O_NONBLOCK));
							//Possible flags:
							//	O_RDONLY - Open for reading only.
							//	O_WRONLY - Open for writing only.
							//	O_NDELAY / O_NONBLOCK (same function) - Enables nonblocking mode. When set read requests on the file can return immediately with a failure status
							//											if there is no input immediately available (instead of blocking). Likewise, write requests can also return
							//											immediately with a failure status if the output can't be written immediately.
		if (our_output_fifo_filestream != -1)
			cout << "Opened output FIFO: " << our_output_fifo_filestream << endl;
		
		//----- READ KEYBOARD EVENTS -----
		while (NoError)
		{
			ReadDevice = read(FileDevice, InputEvent, sizeof(struct input_event) * 64);

			if (ReadDevice < (int) sizeof(struct input_event))
			{
				//This should never happen
				//perror("KeyboardMonitor error reading - keyboard lost?");
				NoError = 0;
			}
			else
			{
				for (Index = 0; Index < ReadDevice / sizeof(struct input_event); Index++)
				{
					//We have:
					//	InputEvent[Index].time		timeval: 16 bytes (8 bytes for seconds, 8 bytes for microseconds)
					//	InputEvent[Index].type		See input-event-codes.h
					//	InputEvent[Index].code		See input-event-codes.h
					//	InputEvent[Index].value		01 for keypress, 00 for release, 02 for autorepeat

					if (InputEvent[Index].type == EV_KEY)
					{
						ss1.clear();
						ss1.str(std::string());
						
						if (InputEvent[Index].value == 2)
						{
							//This is an auto repeat of a held down key
							//ss1 << (int)(InputEvent[Index].code) << " Auto Repeat";
						}
						else if (InputEvent[Index].value == 1)
						{
							//----- KEY DOWN -----
							ss1 << (int)(InputEvent[Index].code) << " Key Down";	//input-event-codes.h
						}
						else if (InputEvent[Index].value == 0)
						{
							//----- KEY UP -----
							ss1 << (int)(InputEvent[Index].code) << " Key Up";		//input-event-codes.h
						}
						
						//----- WRITE EVENT TO THE OUTPUT FIFO -----
						write(our_output_fifo_filestream, (void*)ss1.str().c_str(), strlen(ss1.str().c_str()));
						
					}
				}
			}
		} 
		close(FileDevice);
		cout << "KeyboardMonitor child thread ending!" << endl;
		_exit(0);		//Don't forget to exit!
	}

}

//**********************************************
//**********************************************
//********** KEYBOARD MONITOR PROCESS **********
//**********************************************
//**********************************************
//Call from main loop
void KeyboardMonitorProcess (void)
{
	//---------------------------------------------
	//----- CHECK FIFO FOR ANY RECEIVED BYTES -----
	//---------------------------------------------
	// Read up to 255 characters from the port if they are there
	if (our_input_fifo_filestream != -1)
	{
		unsigned char InputBuffer[256];
		int BufferLength = read(our_input_fifo_filestream, (void*)InputBuffer, 255);		//Filestream, buffer to store in, number of bytes to read (max)
		if (BufferLength < 0)
		{
			//An error occured (this can happen)
		}
		else if (BufferLength == 0)
		{
			//No data waiting
		}
		else
		{
			//Bytes received
			InputBuffer[BufferLength] = '\0';
			cout << InputBuffer << endl;
		}
	}
}
USEFUL?
We benefit hugely from resources on the web so we decided we should try and give back some of our knowledge and resources to the community by opening up many of our company’s internal notes and libraries through mini sites like this. We hope you find the site helpful.
Please feel free to comment if you can add help to this page or point out issues and solutions you have found, but please note that we do not provide support on this site. If you need help with a problem please use one of the many online forums.

Comments

Your email address will not be published. Required fields are marked *