Sunday, June 26, 2022
HomeSoftware DevelopmentMapping Occasion Codes to Keys in Python

Mapping Occasion Codes to Keys in Python


Welcome again to the third and remaining half in our collection exhibiting builders work with non-blocking enter in Python. Within the first a part of this collection we realized gather enter and occasion knowledge with C/C++ and the place the required information had been positioned in Linux. Within the second a part of our tutorial collection, we used Python to learn keyboard occasions. On this remaining half, we are going to lastly map occasion codes to keys and full our instance Python program.

You may learn the primary two half in our Python tutorial collection right here:

One remaining observe earlier than we proceed: the code examples demonstrated on this programming tutorial collection will use Python 3.9.2 and run on a Raspberry Pi 4 Mannequin B (which you should buy from the included hyperlink); nonetheless, this code must be moveable to virtually any Linux system which helps Python 3. To that finish, this information will embrace demonstrations of code in a Kali Linux setting operating Python 3.9.12. The explanation for selecting the Raspberry Pi as the first demonstration gadget has extra to do with extending the performance of this code in a future article.

Learn how to Learn Keyboard Occasions in Python

So, whereas occasion codes are good, it could be even nicer to map these occasions to keys on the keyboard. The key codes are constants that are saved in /usr/embrace/linux/input-event-codes.h. Beneath is a sampling of those values:

Linux Event Key Codes

Determine 1 – Key Codes in Raspberry Pi OS

Event Key Codes in Kali Linux


Determine 2 – Key Codes in Kali Linux

Now, whereas it usually is just not a secure assumption that every one the constants will all the time be the identical values in each Linux setting, it’s seemingly a “safer” assumption to make than most, as a number of issues would break if these values had been completely different between Linux environments.

The earlier Python code we labored on within the prior tutorial already consists of one instance of mapping. The ESC secret’s mapped from its key code of 1. Nonetheless, different keys can be mapped. The Python code under consists of logic to parse out the mappings immediately from /usr/embrace/linux/input-event-codes.h:

#demo-keyboard-mappings.py

import struct
import sys
from datetime import datetime

def GetKeyboardEventFile(tokenToLookFor):
	# Any exception raised right here can be processed by the calling operate.
	part = ""
	line = ""
	eventName = ""

	fp = open ("/proc/bus/enter/units", "r")
	carried out = False
	whereas False == carried out:
		line = fp.readline()
		if line:
			#print (line.strip())
			if "" == line.strip():
				#print ("nFound Part:n" + part)
				if -1 != part.discover(tokenToLookFor) and -1 == part.decrease().discover("mouse"):
					# It's totally doable there to be a number of units
					# listed as a keyboard. On this case, I'll search for 
					# the phrase "mouse" and exclude something that accommodates
					# that. This part could have to be suited to style
					print ("Discovered [" + tokenToLookFor + "] in:n" + part)
					# Get the final a part of the "Handlers" line:
					traces = part.break up('n')
					for sectionLine in traces:
						# The strip() methodology is required as a result of there could also be trailing areas
						# on the finish of this line. This can confuse the break up() methodology.
						if -1 != sectionLine.strip().discover("Handlers=") and "" == eventName:
							print ("Discovered Handlers line: [" + sectionLine + "]")
							sectionLineParts = sectionLine.strip().break up(' ')
							eventName = sectionLineParts[-1]
							print ("Discovered eventName [" + eventName + "]")
							carried out = True
				part = ""
			else:
				part = part + line
		else:
			carried out = True
	fp.shut()

	if "" == eventName:
		elevate Exception("No occasion title was discovered for the token [" + tokenToLookFor + "]")

	return "/dev/enter/" + eventName

def MapCodeToKey (eventCodeStr):
	keyName = ""
	attempt:
		# What? You thought I used to be going to kind in all of the codes?
		fp = open ("/usr/embrace/linux/input-event-codes.h", "r")

		carried out = False
		whereas False == carried out and "" == keyName:
			line = fp.readline()
			if line:
				# Search for traces that solely comprise KEY_ as different constants have the identical numbers as values.
				if -1 != line.strip().discover("KEY_") and line.strip().endswith(eventCodeStr):
					# Crude however efficient, simply break up the road by areas and take the second worth.
					lineParts = line.strip().break up(' ')
					# Notice that the road *could* be tab-delimited.
					keyName = lineParts[1].strip().break up('t')[0]
			else:
				carried out = True
		fp.shut()
	besides Exception as err:
		# Not a deal-breaker, however an error must be reported.
		print ("Cannot learn from file /usr/embrace/linux/input-event-codes.h resulting from error [" + str(err) + "]")
	return keyName

def principal(argv):
	# Want so as to add code which figures out the title of this file from 
	# /proc/bus/enter/units - Search for EV=120013
	# Per Linux docs, 120013 is a hex quantity indicating which sorts of occasions
	# this gadget helps, and this quantity occurs to incorporate the keyboard
	# occasion.

	keyboardEventFile = ""
	attempt:
		keyboardEventFile = GetKeyboardEventFile("EV=120013");
	besides Exception as err:
		print ("Could not get the keyboard occasion file resulting from error [" + str(err) + "]")

	if "" != keyboardEventFile:
		attempt:
			ok = open (keyboardEventFile, "rb");
			# The struct format reads (small L) (small L) (capital H) (capital H) (capital I)
			# Per Python, the construction format codes are as follows:
			# (small L) l - lengthy
			# (capital H) H - unsigned brief
			# (capital I) I - unsigned int
			structFormat="llHHI"
			eventSize = struct.calcsize(structFormat)

			occasion = ok.learn(eventSize)
			goingOn = True
			whereas goingOn and occasion:
				(seconds, microseconds, eventType, eventCode, worth) = struct.unpack(structFormat, occasion)

				# Per Linux docs at https://www.kernel.org/doc/html/v4.15/enter/event-codes.html
				# Constants outlined in /usr/embrace/linux/input-event-codes.h 
				# EV_KEY (1) fixed signifies a keyboard occasion. Values are:
				# 1 - the hot button is depressed.
				# 0 - the hot button is launched.
				# 2 - the hot button is repeated.

				# The code corresponds to which secret's being pressed/launched.

				# Occasion codes EV_SYN (0) and EV_MSC (4) seem however aren't used, though EV_MSC could 
				# seem when a state modifications.

				unixTimeStamp = float(str(seconds) + "." + str(microseconds)) 
				utsDateTimeObj = datetime.fromtimestamp(unixTimeStamp)
				friendlyDTS = utsDateTimeObj.strftime("%B %d, %Y - %H:%M:%S.%f")

				if 1 == eventType:
					keyName = MapCodeToKey (str (eventCode))
					# It's essential to flush the print assertion or else holding a number of keys down
					# is more likely to block *output*
					print ("Occasion Measurement [" + str(eventSize) + "] Kind [" + str(eventType) + "], code [" +
					str (eventCode) + "], key title [" + keyName + "], worth [" + str(value) + "] at [" + friendlyDTS + "]", flush=True)
				if 1 == eventCode:
					print ("ESC Pressed - Quitting.")
					goingOn = False
				#if 4 == eventType:
				#	print ("-------------------- Separator Occasion 4 --------------------")
				occasion = ok.learn(eventSize)

			ok.shut()
		besides IOError as err:
			print ("Cannot open keyboard enter file because of the error [" + str(err) + "]. Possibly attempt sudo?")
		besides Exception as err:
			print ("Cannot open keyboard enter file resulting from another error [" + str(err) + "].")
	else:
		print ("No keyboard enter file may very well be discovered.")
	return 0

if "__main__" == __name__:
	principal(sys.argv[1:])



Itemizing 4 - Studying the keyboard Enter and mapping it to precise keys.

Notice that there isn’t a “unusual magic” right here. The Python code is just treating the C Header file as a textual content file and parsing it as such. It’s not “compiling in” the values within the C Header file.

Beneath is a few pattern output, with annotations indicating particular key sequences being pressed:

Python and Raspberry Pi

Determine 3 – Pattern Out with Key Names in Raspberry Pi

Extra pattern output, this time in Kali Linux:

Raspberry Pi Key Codes

Determine 4 – Pattern Out with Key Names in Kali Linux

Learn: Primary Electronics Management with the Raspberry Pi and Python

Deciphering Key Sequences and Mixtures in Python

As could be seen, combos of keys pressed, akin to modifiers together with Shift, Ctrl, or Alt, could be captured together with the keys they modify. It’s simply vital to notice that all the occasions pertaining to each keys being pressed have to be captured and interpreted appropriately. Within the examples above, each occasions indicating {that a} modifier key has been depressed (1) or held down (2) are getting used.

Additionally observe that utilizing a modifier key doesn’t change the worth of the important thing code that it modifies. As an illustration, the A key that’s pressed together with Shift may produce a capital A when typed, however from the Working System’s perspective, it’s merely two keycodes being despatched out as occasions.

Additional, not one of the code samples can seize Ctrl-Z, Ctrl-C or another key sequence that both pushes the code execution into the background, or causes an interrupt exception, with out including additional logic to deal with such capabilities.

Safety Implications

The examples which run on Kali make use of root entry. Nonetheless, as was proven on the Raspberry Pi gadget, this isn’t required (supplied that the pi account was used). Notice the group membership of the occasion information:

Group Assignments Raspberry Pi

Determine 5 – Default Group Assignments of Enter Occasion Information in Kali Linux

Discover how every of the occasion enter information is assigned to the group “enter”. The identical group task is current on the Raspberry Pi gadget:

Group Assignments of Input Events in Linux

Determine 6 – Default Group Assignments of Enter Occasion Information in Raspberry Pi OS

The distinction is that the pi account on the Raspberry Pi gadget is a part of the “enter” group, however the “phil” account is just not:

Input Group Members in Raspberry Pi

Determine 7 – “enter” Group Members in Raspberry Pi OS, together with the pi consumer account

Python and Raspberry Pi Input Tutorial

Determine 8 – “enter” Group Membership, with the phil account not current.

It’s doable to easily assign a consumer a type of privileged entry by assigning that consumer to the “enter” group, however this will result in different safety points, probably the most notable being that the information in /dev/enter listing present any inputs generated by any consumer. Whereas this might not be a difficulty with a single consumer logged right into a desktop session, it will possibly current different main safety issues if there are a number of customers who share the identical system. Don’t forget that there are applied sciences that make it doable for a number of customers to concurrently get desktop entry to a system. A consumer operating these scripts with privileged entry would have the ability to see what different customers enter into the keyboard.

There do exist various libraries – ncurses being probably the most distinguished instance – which might present non-root entry to keyboard inputs, however these are past the scope of an introductory tutorial.

Closing Ideas on Python and Raspberry Pi Enter

With the ability to learn keyboard enter in a non-blocking method in Python opens the door to having the ability to entice hotkeys and multi-key combos that blocking applications can not seize. Even the time intervals between key presses can be utilized to find out what types of issues a program can do, akin to performing an motion quicker or slower. Past simply gaming, all types of different interactive purposes could be developed utilizing this coding construction, despite the restrictions imposed by platform dependency. Fashionable event-based coding in languages akin to Java or C# relies partially on the ideas explored on this tutorial. And, on a purely editorial observe, if a developer needed to develop an utility which used non-blocking inputs on the Home windows platform, it could make extra sense to make use of Java or C# for that goal as a substitute of Python, as these languages have already got all of the event-based hooks in-built already.

However, in sticking again to Linux, the identical logic offered on this article can apply to different units whose information are listed within the /dev/enter listing, though they don’t return occasions in the identical method that the keyboard does. As an illustration, a gamepad plugged into the Raspberry Pi could return completely different occasion values every time the code is run to guage it, so additional processing could also be essential to correctly incorporate enter from these units into different applications.

Learn extra Python programming tutorials and software program growth guides.

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments