Individuare i tasti della tastiera intercettando i dati grezzi dal suo file-device
E' possibile intercettare i dati grezzi provenienti dalla tastiera, per individuare di ciascun tasto premuto il suo Codice di Scansione.
Per gestire direttamente i dati grezzi della tastiera, bisogna innanzitutto individuare il suo file-device.
Sappiamo che esso è presente nel percorso /dev/input/, ed è rappresentato da uno dei file con il nome eventN; laddove "N" è un suffisso del nome rappresentato da un numero. [nota 1]
Per individuare precisamente quale sia il file-device associato alla tastiera, fra quelli che iniziano per event, bisognerà interrogare il file:
/proc/bus/input/devices
Tale file mostra quali dispositivi e gestori di input sono attualmente attivi, ed all'interno del quale bisognerà individuare la riga che inizia per "H:", e che rappresenta i driver gestore associati ad un determinato dispositivo. Pertanto, la riga "H:" che si riferisce alla tastiera si presenta visivamente come segue:
H: Handlers=sysrq kbd eventn
laddove n è in realtà un numero. [nota 2]
Individuato, dunque, il file-device associato alla tastiera, lo gestiremo con una variabile di tipo File o di tipo Process.
Ogni volta che è premuto un tasto della tastiera, vengono scritti nel file-device della tastiera alcuni dati, che potranno essere ovviamente letti dal programma Gambas.
Il 21° dato ci darà il "Codice di Scansione" del tasto premuto. Tale codice, identificando univocamente il tasto appena premuto o rilasciato, è dunque universale per tutti i calcolatori, e quindi tranquillamente utilizzabile nei nostri programmi Gambas.
Codice esemplificativo in ambiente grafico con le sole risorse di Gambas
Mostriamo di seguito un possibile codice esemplificativo per intercettare e ricavare in ambiente grafico di Gambas, per ciascun tasto premuto, il "Codice di Scansione"
Supponiamo, nell'esempio che segue, che sia stato già individuato il file-device corrispondente alla tastiera.
Tale file-device sarà innazitutto privato della sua protezione e quindi sarà aperto in "lettura", gestito con una variabile di tipo File e posto sotto osservazione con la parola-chiave Watch.
I dati letti dal file-device saranno caricati in una variabile di tipo Stringa, dalla quale sarà estrapolato il 21° dato-valore.
Private fl As File
Public Sub Form_Open()
' Supponiamo che file-device da leggere individuato nel file "/proc/bus/input/devices" sia "/dev/input/event4".
' Eliminiamo innanzitutto la sua protezione:
Shell "echo " & InputBox("Immettere la propria PASSWORD di sistema:") & " | sudo -S chmod 444 /dev/input/event4" Wait
' Quindi lo apriamo in "lettura" e lo poniamo sotto "osservazione":
fl = Open "/dev/input/event4" For Read Watch
End
Public Sub File_Read()
Dim s As String
TextArea1.Text &= gb.NewLine
Read #fl, s, -256
TextArea1.Text &= "Codice di scansione del tasto premuto o rilasciato: " & Asc(s, 21) & " = "
End
Public Sub Form_Close()
fl.Close
End
Come il precedente, ma in applicazione a riga di comando:
Private fl As File Public Sub Main() ' Con questa riga di comando viene eliminata la protezione al file-device da leggere. ' E' necessario porre la propria "password" di sistema al posto della parola "MIA_PASSWORD": Shell "echo MIA_PASSWORD | sudo -S chmod 444 /dev/input/event4" Wait fl = Open "/dev/input/event4" For Read Watch End Public Sub File_Read() Dim s As String Read #fl, s, -256 Print "Codice di scansione del tasto premuto: " & Asc(s, 21) End Public Sub Application_Read() ' Se si preme il tasto "Invio" della tastiera, viene sollevato questo Evento fl.Close ' Avendo sollevato l'Evento "Application_Read()", per chiudere l'applicazione è necessario usare l'istruzione "Quit": Quit End
Usando la funzione Exec, il comando bash cat e la Classe Process
In quest'altro esempio è necessario attivare il Componente gb.desktop: si provvederà da codice ad individuare e successivamente a eliminare la protezione del file-device corrispondente alla tastiera.
Il file-device relativo alla tastiera sara gestito con le risorse della Classe Process.
Per ogni tasto che viene premuto, vengono intercettati dal file-device della tastiera ben 144 dati; il 21° ci darà il codice di scansione del tasto premuto della tastiera.
Private pr As Process
Private lett As New String[]
Public Sub Form_Open()
Dim s As String
Dim i As Integer
s = File.Load("/proc/bus/input/devices")
' Individuiamo la riga contenente il testo "Handlers=sysrq"; ed individuiamo il nome del file-device della tastiera:
s = Trim(Scan(s, "*Handlers=sysrq *\n*")[1])
i = InStr(s, "event")
s = Mid(s, i, 6) ' ...oppure: s[i - 1, 6]
' Per leggere il file-device individuato, bisogna avere i necessari permessi.
' Pertanto, se non sono stati modificati i permessi del file-device, allora vengono modificati (bisognerà inserire la propria password):
If Stat("/dev/input" &/ s).Auth <> "r--r--r--" Then
Desktop.RunAsRoot("sudo -S chmod 444 /dev/input" &/ s)
Endif
' Poniamo un'attesa mediante un ciclo, fino a che i permessi del file-device non saranno mutati:
Do
Wait 0.01
Loop Until Stat("/dev/input" &/ s).Auth = "r--r--r--"
' Finalmente il file-device viene gestito mediante un "Processo":
pr = Exec ["cat", "/dev/input" &/ s] For Read As "Processo"
End
Public Sub Processo_Read()
Dim b As Byte
While Not Eof(pr)
' Legge i dati provenienti dal file-device della tastiera:
Read #pr, b
lett.Push(CStr(b))
If lett.Count = 144 Then
' Legge l'elemento di numero d'indice 20 della variabile array:
Write "\e[0mCodice scansione tasto: \e[1m" & lett[20]
Print
lett.Clear
Endif
Wend
End
Simile al precedente, ma in applicazione a riga di comando
Mostriamo di seguito un possibile codice esemplificativo per intercettare e ricavare nell'ambito di un'applicazione Gambas a riga di comando, per ciascun tasto premuto, il codice di scansione.
Private pr As Process
Private lett As New String[]
Public Sub Main()
Dim s As String
Dim i As Integer
s = File.Load("/proc/bus/input/devices")
' Individuiamo la riga contenente il testo "Handlers=sysrq"; ed individuiamo il nome del file-device della tastiera:
s = Trim(Scan(s, "*Handlers=sysrq *\n*")[1])
i = InStr(s, "event")
s = Mid(s, i, 6)
' Per leggere il file-device individuato, bisogna avere i necessari permessi. Essi vengono quindi modificati.
' E' necessario impostare la password personale dell'utente.
Shell "echo MIA-PASSWORD | sudo -S chmod 444 /dev/input" &/ s Wait
Print
' Finalmente il file-device viene gestito mediante un "Processo":
pr = Exec ["cat", "/dev/input" &/ s] For Read As "Processo"
End
Public Sub Processo_Read()
Dim b As Byte
While Not Eof(pr)
' Legge i dati provenienti dal file-device della tastiera:
Read #pr, b
lett.Push(CStr(b))
Select Case lett.Count
Case 72
Write "\e[0mTasto: \e[1m"
Case 144
' Legge l'elemento di numero d'indice 20 della variabile array:
Write "\e[0m - Codice scansione tasto: \e[1m" & lett[20]
Print
lett.Clear
End Select
Flush
Wend
End
Usando la funzione esterna "ioctl()"
In questo esempio useremo la funzione esterna "ioctl()":
Library "libc:6"
Public Struct timeval
tv_sec As Long
tv_usec As Long
End Struct
Public Struct input_event
time_ As Struct Timeval
type As Short
code As Short
value As Integer
End Struct
Private Const _IOC_NRSHIFT As Integer = 0
Private Const _IOC_NRBITS As Integer = 8
Private _IOC_TYPESHIFT As Long = _IOC_NRSHIFT + _IOC_NRBITS
Private Const _IOC_TYPEBITS As Long = 8
Private _IOC_SIZESHIFT As Long = _IOC_TYPESHIFT + _IOC_TYPEBITS
Private Const _IOC_SIZEBITS As Long = 14
Private _IOC_DIRSHIFT As Long = _IOC_SIZESHIFT + _IOC_SIZEBITS
Private Const _IOC_WRITE As Long = 1
Private Const _IOC_READ As Long = 2
' int ioctl(int __fd, unsigned long int __request, ...)
' Perform the I/O control operation specified by REQUEST on FD.
Private Extern ioctl_name(__fd As Integer, __request As Long, arg As Byte[]) As Integer Exec "ioctl"
Private Extern ioctl_grab(__fd As Integer, __request As Long, arg As Integer) As Integer Exec "ioctl"
' ssize_t read (int __fd, void *__buf, size_t __nbytes)
' Read NBYTES into BUF from FD.
Private Extern read_C(__fd As Integer, __buf As Input_event, __nbytes As Long) As Long Exec "read"
Private ke As New Input_event
Private kfl As File
Public Sub Main()
Dim keyboard_name As New Byte[256]
Dim rit As Long
' Supponendo che il file-device attinente alla tastiera sia "event4".
' Con questa riga di comando viene eliminata la protezione al file-device da leggere.
' E' necessario porre la propria "password" di sistema al posto della parola "MIA_PASSWORD":
Shell "echo MIA_PASSWORD | sudo -S chmod 444 /dev/input/event4" Wait
' Quindi lo si apre in "Lettura" e lo si pone in "osservazione":
kfl = Open "/dev/input/event4" For Read Watch
If kfl.Handle == -1 Then
kfl.Close
Error.Raise("Error !")
Endif
ioctl_name(kfl.Handle, Eviocname(Asc("E"), &h06, keyboard_name.Count), keyboard_name)
Print "Reading From: \e[34m"; keyboard_name.ToString(0, keyboard_name.Find(0))
rit = ioctl_grab(kfl.Handle, Eviocgrab(Asc("E"), &h90, SizeOf(gb.Integer)), 1)
Print "\e[0mGetting exclusive access: "; IIf(rit == 0, "\e[32mSUCCESS\e[0m", "\e[31mFAILURE\e[0m")
Print
End
Public Sub File_Read()
Dim rit As Long
rit = read_C(kfl.Handle, ke, Object.SizeOf(ke))
If rit > -1 Then
Print "\e[1mtime\e[0m: "; ke.time_.tv_sec; " , \e[1mtype\e[0m: "; ke.type &
" , \e[1mcode\e[0m: "; ke.code; " , \e[1mvalue\e[0m: "; ke.value
' Se viene premuto il tasto "Esc", il programma viene chiuso:
If (ke.type == 4) And (ke.code == 4) And (ke.value == 1) Then kfl.Close
Endif
End
Private Function Eviocname(type As Long, nr As Long, size As Long) As Long
Return Shl(_IOC_READ, _IOC_DIRSHIFT) Or Shl(type, _IOC_TYPESHIFT) Or Shl(nr, _IOC_NRSHIFT) Or Shl(size, _IOC_SIZESHIFT)
End
Private Function Eviocgrab(type As Long, nr As Long, size As Long) As Long
Return Shl(_IOC_WRITE, _IOC_DIRSHIFT) Or Shl(type, _IOC_TYPESHIFT) Or Shl(nr, _IOC_NRSHIFT) Or Shl(size, _IOC_SIZESHIFT)
End
Note
[1] Il nome event è dato dalla circostanza che il rapporto tra i vari elementi del sottosistema di input - ossia gli ingressi principali, i driver e gestori di eventi - avviene attraverso eventi.
[2] Nelle versioni di Mint superiori alla 17 dopo la parola "eventn" è presente anche la parola "leds". Pertanto, se ne dovrà tenere conto ai fini dell'individuazione della parola "eventn".