Python’da Olay Kodlarını Anahtarlarla Eşleştirme

Python'da Olay Kodlarını Anahtarlarla Eşleştirme

Geliştiricilere Python’da engellenmeyen girdilerle nasıl çalışacaklarını gösteren serimizin üçüncü ve son bölümüne tekrar hoş geldiniz. Bu serinin ilk bölümünde, C/C++ ile girdi ve olay verilerinin nasıl toplanacağını ve gerekli dosyaların Linux’ta nerede bulunduğunu öğrendik. Eğitim serimizin ikinci bölümünde klavye olaylarını okumak için Python kullandık. Bu son bölümde, son olarak olay kodlarını anahtarlara eşleyeceğiz ve örnek Python programımızı tamamlayacağız.

Python eğitim serimizin ilk iki bölümünü buradan okuyabilirsiniz:

Devam etmeden önce son bir not: Bu programlama eğitim serisinde gösterilen kod örnekleri Python 3.9.2’yi kullanacak ve bir Raspberry Pi 4 Model B (dahil edilen bağlantıdan satın alabilirsiniz); ancak, bu kod Python 3’ü destekleyen hemen hemen tüm Linux sistemlerine taşınabilir olmalıdır. Bu amaçla, bu kılavuz Python 3.9.12 çalıştıran Kali Linux ortamında kod gösterimlerini içerecektir. Raspberry Pi’yi birincil tanıtım cihazı olarak seçmenin nedeni, bu kodun işlevselliğini gelecekteki bir makalede genişletmekle daha fazla ilgilidir.

Python’da Klavye Olayları Nasıl Okunur?

Yani olay kodları güzel olsa da, bu olayları klavyedeki tuşlara eşlemek daha da güzel olurdu. bu anahtar kodlar içinde saklanan sabitlerdir. /usr/include/linux/input-event-codes.h. Aşağıda bu değerlerin bir örneği verilmiştir:

Şekil 1 – Raspberry Pi OS’deki Anahtar Kodlar

Kali Linux'ta Olay Anahtar Kodları


Şekil 2 – Kali Linux’ta Anahtar Kodlar

Şimdi, normalde değil tüm sabitlerin her Linux ortamında her zaman aynı değerler olacağı konusunda güvenli bir varsayım, bu Bu değerler Linux ortamları arasında farklı olsaydı, birçok şey bozulacağından, muhtemelen çoğundan daha “güvenli” bir varsayım.

Önceki eğitimde üzerinde çalıştığımız önceki Python kodu zaten bir eşleme örneği içeriyor. bu ESC anahtar, anahtar kodundan eşlenir 1. Ancak, diğer tuşlar da eşlenebilir. Aşağıdaki Python kodu, eşlemeleri doğrudan /usr/include/linux/input-event-codes.h:

#demo-keyboard-mappings.py

import struct
import sys
from datetime import datetime

def GetKeyboardEventFile(tokenToLookFor):
	# Any exception raised here will be processed by the calling function.
	section = ""
	line = ""
	eventName = ""

	fp = open ("/proc/bus/input/devices", "r")
	done = False
	while False == done:
		line = fp.readline()
		if line:
			#print (line.strip())
			if "" == line.strip():
				#print ("nFound Section:n" + section)
				if -1 != section.find(tokenToLookFor) and -1 == section.lower().find("mouse"):
					# It is entirely possible there to be multiple devices
					# listed as a keyboard. In this case, I will look for 
					# the word "mouse" and exclude anything that contains
					# that. This section may need to be suited to taste
					print ("Found [" + tokenToLookFor + "] in:n" + section)
					# Get the last part of the "Handlers" line:
					lines = section.split('n')
					for sectionLine in lines:
						# The strip() method is needed because there may be trailing spaces
						# at the end of this line. This will confuse the split() method.
						if -1 != sectionLine.strip().find("Handlers=") and "" == eventName:
							print ("Found Handlers line: [" + sectionLine + "]")
							sectionLineParts = sectionLine.strip().split(' ')
							eventName = sectionLineParts[-1]
							print ("Found eventName [" + eventName + "]")
							done = True
				section = ""
			else:
				section = section + line
		else:
			done = True
	fp.close()

	if "" == eventName:
		raise Exception("No event name was found for the token [" + tokenToLookFor + "]")

	return "/dev/input/" + eventName

def MapCodeToKey (eventCodeStr):
	keyName = ""
	try:
		# What? You thought I was going to type in all the codes?
		fp = open ("/usr/include/linux/input-event-codes.h", "r")

		done = False
		while False == done and "" == keyName:
			line = fp.readline()
			if line:
				# Look for lines that only contain KEY_ as other constants have the same numbers as values.
				if -1 != line.strip().find("KEY_") and line.strip().endswith(eventCodeStr):
					# Crude but effective, just split the line by spaces and take the second value.
					lineParts = line.strip().split(' ')
					# Note that the line *may* be tab-delimited.
					keyName = lineParts[1].strip().split('t')[0]
			else:
				done = True
		fp.close()
	except Exception as err:
		# Not a deal-breaker, but an error should be reported.
		print ("Can't read from file /usr/include/linux/input-event-codes.h due to error [" + str(err) + "]")
	return keyName

def main(argv):
	# Need to add code which figures out the name of this file from 
	# /proc/bus/input/devices - Look for EV=120013
	# Per Linux docs, 120013 is a hex number indicating which types of events
	# this device supports, and this number happens to include the keyboard
	# event.

	keyboardEventFile = ""
	try:
		keyboardEventFile = GetKeyboardEventFile("EV=120013");
	except Exception as err:
		print ("Couldn't get the keyboard event file due to error [" + str(err) + "]")

	if "" != keyboardEventFile:
		try:
			k = open (keyboardEventFile, "rb");
			# The struct format reads (small L) (small L) (capital H) (capital H) (capital I)
			# Per Python, the structure format codes are as follows:
			# (small L) l - long
			# (capital H) H - unsigned short
			# (capital I) I - unsigned int
			structFormat="llHHI"
			eventSize = struct.calcsize(structFormat)

			event = k.read(eventSize)
			goingOn = True
			while goingOn and event:
				(seconds, microseconds, eventType, eventCode, value) = struct.unpack(structFormat, event)

				# Per Linux docs at https://www.kernel.org/doc/html/v4.15/input/event-codes.html
				# Constants defined in /usr/include/linux/input-event-codes.h 
				# EV_KEY (1) constant indicates a keyboard event. Values are:
				# 1 - the key is depressed.
				# 0 - the key is released.
				# 2 - the key is repeated.

				# The code corresponds to which key is being pressed/released.

				# Event codes EV_SYN (0) and EV_MSC (4) appear but are not used, although EV_MSC may 
				# appear when a state changes.

				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 is necessary to flush the print statement or else holding multiple keys down
					# is likely to block *output*
					print ("Event Size [" + str(eventSize) + "] Type [" + str(eventType) + "], code [" +
					str (eventCode) + "], key name [" + keyName + "], value [" + str(value) + "] at [" + friendlyDTS + "]", flush=True)
				if 1 == eventCode:
					print ("ESC Pressed - Quitting.")
					goingOn = False
				#if 4 == eventType:
				#	print ("-------------------- Separator Event 4 --------------------")
				event = k.read(eventSize)

			k.close()
		except IOError as err:
			print ("Can't open keyboard input file due to the error [" + str(err) + "]. Maybe try sudo?")
		except Exception as err:
			print ("Can't open keyboard input file due to some other error [" + str(err) + "].")
	else:
		print ("No keyboard input file could be found.")
	return 0

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



Listing 4 - Reading the keyboard Input and mapping it to actual keys.

Burada “garip bir sihir” olmadığına dikkat edin. Python kodu, C Başlık dosyasını bir metin dosyası olarak ele almak ve bu şekilde ayrıştırmaktır. C Header dosyasındaki değerleri “derlemek” değildir.

Aşağıda, belirli tuş dizilerine basıldığını gösteren ek açıklamalarla birlikte bazı örnek çıktılar verilmiştir:

Python ve Ahududu Pi

Şekil 3 – Raspberry Pi’de Anahtar İsimlerle Örnekleme

Bu sefer Kali Linux’ta daha fazla örnek çıktı:

Raspberry Pi Anahtar Kodları

Şekil 4 – Kali Linux’ta Anahtar İsimlerle Örnekleme

Okumak: Raspberry Pi ve Python ile Temel Elektronik Kontrolü

Python’da Anahtar Dizileri ve Kombinasyonları Yorumlama

Görülebileceği gibi, değiştiriciler gibi basılan tuş kombinasyonları Vardiya, Ctrlveya alt, değiştirdikleri tuşlarla birlikte yakalanabilir. Sadece basılan her iki tuşla ilgili tüm olayların yakalanması ve doğru yorumlanması gerektiğine dikkat etmek gerekir. Yukarıdaki örneklerde, bir değiştirici tuşa basıldığını gösteren her iki olay (1) veya basılı tutuldu (2) kullanılıyor.

Ayrıca, değiştirici anahtar kullanmanın, değiştirdiği anahtar kodunun değerini değiştirmediğini unutmayın. Örneğin, A ile birlikte basılan tuş Vardiya sermaye üretebilir A yazıldığında, ancak İşletim Sisteminin bakış açısından, olay olarak gönderilen sadece iki anahtar koddur.

Ayrıca, kod örneklerinin hiçbiri yakalayamaz Ctrl-Z, Ctrl-C veya bu tür işlevleri işlemek için daha fazla mantık eklemeden kod yürütmeyi arka plana iten veya bir kesme istisnasına neden olan başka herhangi bir tuş dizisi.

Güvenlik Etkileri

Kali’de çalışan örnekler şunları kullanır: kök erişim. Ancak Raspberry Pi cihazında gösterildiği gibi bu gerekli değildir (şartlı pi hesap kullanıldı). Olay dosyalarının grup üyeliğine dikkat edin:

Grup Ödevleri Raspberry Pi

Şekil 5 – Kali Linux’ta Girdi Olay Dosyalarının Varsayılan Grup Atamaları

Olay giriş dosyalarının her birinin “input” grubuna nasıl atandığına dikkat edin. Aynı grup ataması Raspberry Pi cihazında da mevcuttur:

Linux'ta Girdi Olaylarının Grup Atamaları

Şekil 6 – Raspberry Pi OS’de Giriş Olay Dosyalarının Varsayılan Grup Atamaları

Fark şu ki, pi Raspberry Pi cihazındaki hesap, “giriş” grup, ancak “fil” hesap değil:

Raspberry Pi'de Giriş Grubu Üyeleri

Şekil 7 – Raspberry Pi OS’de pi kullanıcı hesabı dahil “giriş” Grup Üyeleri

Python ve Raspberry Pi Giriş Eğitimi

Şekil 8 – Phil hesabının bulunmadığı “giriş” Grup Üyeliği.

Bir kullanıcıya bir ayrıcalıklı erişim biçimi atamak, o kullanıcıyı “giriş” grup, ancak bu, diğer güvenlik sorunlarına yol açabilir, en dikkat çekici olanı, içindeki dosyaların /dev/girdi dizin tarafından oluşturulan tüm girdileri gösterir hiç kullanıcı. Bu, bir masaüstü oturumunda oturum açan tek bir kullanıcıyla ilgili bir sorun olmasa da, aynı sistemi paylaşan birden fazla kullanıcı varsa, diğer önemli güvenlik endişelerini ortaya çıkarabilir. Birden fazla kullanıcının aynı anda bir sisteme masaüstü erişimini mümkün kılan teknolojiler olduğunu unutmayın. Bu komut dosyalarını ayrıcalıklı erişimle çalıştıran bir kullanıcı, diğer kullanıcıların klavyeye ne girdiğini görebilir.

Alternatif kütüphaneler var – ncurses en belirgin örnektir – klavye girişlerine root dışı erişim sağlayabilir, ancak bunlar bir giriş öğreticisinin kapsamı dışındadır.

Python ve Raspberry Pi Girişi Üzerine Son Düşünceler

Python’da klavye girişini engellemeden okuyabilmek, kısayol tuşlarını ve engelleme programlarının yakalayamayacağı çok tuşlu kombinasyonları yakalayabilmenin kapısını açar. Tuşlara basışlar arasındaki zaman aralıkları bile, bir eylemi daha hızlı veya daha yavaş gerçekleştirmek gibi bir programın ne tür şeyler yapabileceğini belirlemek için kullanılabilir. Sadece oyun oynamanın ötesinde, platform bağımlılığının getirdiği kısıtlamalara rağmen, bu kodlama yapısı kullanılarak her türlü etkileşimli uygulama geliştirilebilir. Java veya C# gibi dillerde modern olay tabanlı kodlama, kısmen bu öğreticide incelenen kavramlara dayanmaktadır. Ve tamamen editoryal bir not olarak, eğer bir geliştirici Windows platformunda engellenmeyen girdiler kullanan bir uygulama geliştirmek isterse, bu dillerde zaten hepsine sahip olduğundan, bu amaç için Python yerine Java veya C# kullanmak daha mantıklı olacaktır. zaten yerleşik olay tabanlı kancalar.

Ancak, Linux’a geri dönersek, bu makalede sunulan aynı mantık, dosyaları aşağıdaki listede listelenen diğer cihazlar için de geçerli olabilir. /dev/girdi dizini, ancak klavyenin yaptığı gibi olayları döndürmezler. Örneğin, Raspberry Pi’ye takılı bir gamepad, kodu değerlendirmek için her çalıştırıldığında farklı olay değerleri döndürebilir, bu nedenle bu cihazlardan gelen girişi diğer programlara düzgün bir şekilde dahil etmek için daha fazla işlem gerekebilir.

Devamını oku Python programlama eğitimleri ve yazılım geliştirme kılavuzları.

Bir cevap yazın

E-posta hesabınız yayımlanmayacak.