Spinto dall'entusiasmo dei webcast che sto seguendo, ho fatto qualche esperimento per vedere di prendere i tasti premuti dall'utente in modo un po' più veloce rispetto a quanto si fa tramite i classici eventi messi a disposizione del framework. Normalmente, per esempio, si utilizza il seguente codice:
Private Sub Form1_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles MyBase.KeyDown
Select Case e.KeyCode
Case Keys.A
Case Keys.Space
End Select
End Sub
e così via per ogni tasto di cui si vuole recuperare il valore. Però mentre seguivo l'ultimo webcast ho buttato un'occhiata veloce al codice C# che faceva la stessa cosa usando DirectX. Non ricordavo quasi nulla: solo la chiamata ad un metodo che restituisce un array di booleani con 256 elementi. Ogni elemento dell'array contiene True/False a seconda che il tasto sia stato premuto oppure no. Il codice che ho scritto, googlando un po' è:
Dim dx8 As New DxVBLibA.DirectX8
Dim dxinput As DxVBLibA.DirectInput8
Dim rileva As DxVBLibA.DirectInputEnumDevices8
Dim tast As DxVBLibA.DirectInputDeviceInstance8
Dim i As Integer
Dim tast2 As DxVBLibA.DirectInputDevice8
Dim stato As DxVBLibA.DIKEYBOARDSTATE
dxinput = dx8.DirectInputCreate()
rileva = dxinput.GetDIDevices(DxVBLibA.CONST_DI8DEVICETYPE.DI8DEVCLASS_KEYBOARD, DxVBLibA.CONST_DIENUMDEVICESFLAGS.DIEDFL_ATTACHEDONLY)
For i = 1 To rileva.GetCount
tast = rileva.GetItem(i)
Me.lstDevice.Items.Add(tast.GetInstanceName)
Next
La prima parte inizializza un po' di oggetti tirati fuori direttamente da DX8. Lo scopo di questa prima parte è popolare una listbox con l'elenco di tutte le periferiche di tipo tastiera gestibili da DX8. L'oggetto rileva è una sorta di collection contenente tutte le tastiere: ovviamente nei classici PC è sempre una soltanto. Ho provato a modificare i parametri del metodo GetDIDevices ed effettivamente ho recuperato anche il mouse, che però non mi interessava.
Torniamo a noi e alla nostra tastiera.
tast2 = dxinput.CreateDevice(tast.GetGuidProduct)
tast2.SetCommonDataFormat(DxVBLibA.CONST_DICOMMONDATAFORMATS.DIFORMAT_KEYBOARD)
tast2.SetCooperativeLevel(Me.Handle.ToInt32, DxVBLibA.CONST_DISCLFLAGS.DISCL_BACKGROUND Or DxVBLibA.CONST_DISCLFLAGS.DISCL_NONEXCLUSIVE)
tast2 è un oggetto di tipo DirectInputDevice8. Inizializzo la periferica usando il metodo GetGuidProduct di tast. Gli altri metodi non so (in questo momento) dettagliare per bene per mancanza di tempo. Il codice è stato recuperato da google (per la serie, declino ogni responsabilità).
A questo punto comincio un ciclo infinito, un classico Do...While sempre a True.
Do While True
tast2.Acquire()
tast2.GetDeviceStateKeyboard(stato)
For i = 0 To aKeys.Length - 1
aKeys(i) = stato.key(i) <> 0
Next
System.Windows.Forms.Application.DoEvents()
Loop
Ad ogni loop, con la chiamata a GetDeviceStateKeyboard inizializzo la variabile stato. stato mette a disposizione la proprietà key che ritorna True/False per ogni tasto premuto all'ultima rilevazione. Precisazione: io mi sono preparato un bel array aKeys di booleani con 212 elementi (il numero di tasti sulla tastiera?) e setto ciascun elemento come si vede dal codice qui sopra. Se stato.key(i) è diverso da zero, allora quel tasto è stato premuto, e metto l'elemento dell'array a True. Non ho messo un If, ho assegnato direttamente stato.key(i) <> 0.
Credo che in Microsoft ci abbiano pensato bene, però non mi piace una cosa. Mi aspettavo di avere in stato.key(64), per esempio, lo stato della lettera 'A' (codice ASCII), ma non è così. In stato.key(0) ho il tasto ESCAPE (in alto a sinistra), via via mentre mi sposto verso destra e scendendo, trovo tutti gli altri. Questo mi ha obbligato a definirmi una serie di costanti globali tipo:
Public Const DIK_ESCAPE = 1
Public Const DIK_1 = 2
Public Const DIK_2 = 3
e quindi scrivere codice più leggibile. Cioè, se dentro il loop volessi visualizzare in una label i tasti premuti, sarebbe un po' complicato, perchè non c'è una relazione diretta tra la posizione dell'elemento nell'array e il tasto corrispondente. Dovrei fare una Function con un enorme Select Case. Oppure un modo migliore che (non ci vuole un genio) sto già preparando.