PDA

View Full Version : [JAVA] ascoltare eventi da tastiera


jsdesign
15-07-2009, 13:15
come posso riuscire ad ascoltare qualsiasi evento da tastiera senza attribuire l'ascoltatore di eventi ad un componente di un frame?? L'idea di base è quella di far girare l'applicazione in background e registrare tutti i tasti premuti, come un key logger. Dovrei usare questa funzione per permettere ad un'applicazione di comparire e scomparire tramite la pressione di un tasto.

Grazie per la risposta.

PGI-Bis
15-07-2009, 14:14
Non c'è una funzione ad hoc nelle librerie standard. Devi usare JNI.

Di solito però per funzioni del genere si usa l'area di notifica (TrayIcon).

jsdesign
15-07-2009, 14:55
potresti spiegarti meglio...? io ho bisogno di questo servizio... come faccio?

banryu79
15-07-2009, 15:40
potresti spiegarti meglio...? io ho bisogno di questo servizio... come faccio?
Come ha già detto PGI-bis, dovrai usare la tecnologia JNI.
Questo perchè ti tocca agire a livello di sistema, visto che vuoi ascoltare tutti gli eventi tastiera che (appunto) arrivano al sistema operativo.

Normalmente per ascoltare tali eventi su di un frame AWT o Swing basta apoggiarsi alle utilità fornite dal JDK perchè gli eventi sono prodotti appunto da un componente AWT/Swing che esiste dentro il contesto della tua applicazione che gira nella JVM.
Alla fine il componente GUI che usi si realizza grazie alla presenza di un "peer" nativo: questo fa da ponte grazie alla JVM con il sitema operativo sottostante, il quale smista gli eventi, e fa arrivare alle finestre native gli eventi che a queste interessano; dalle finestre native, tramite i "peer" si arriva al frame Java: ed è per questo che gli eventi del mouse o della tastiera arrivano ai componenti AWT/Swing.

Tu invece vuoi catturare eventi che non sono prodotti/mirati su nessun componente: non c'è appunto una funzione apposita nella libreria standard, perchè normalmente la cattura degli eventi generati dal sistema (nel constesto di AWT) è ristretta a quegli eventi che il sistema stesso mappa sulle finestre grafiche.


L'altra obiezione che ti poneva PGI-bis, se non ho frainteso, era che per fare quello che hai descritto, cioè:
- hai una applicazione Java in esecuzione;
- hai bisogno di farla sparire/riapparire;

si usa una apposita java.awt.TrayIcon (che ha la posiibilità di ascoltare eventi del mouse su se stessa) da piazzare sulla java.awt.SystemTray ("binding" lato Java della System Tray del sistema su cui gira la JVM, se esistente nel sistema stesso, in Windows per esempio c'è :D)

@EDIT:
Spiegazione molto rozza, spero di non aver scritto castronerie.

jsdesign
15-07-2009, 16:37
in c# si potrebbe fare quindi...

PGI-Bis
15-07-2009, 16:45
In C# sicuramente sì. Le API di C# sono le API di Windows, ci mancherebbe che si fossero dimenticati dei pezzi :D.

Cerca "SetWindowHookEx" o una roba del genere.

jsdesign
15-07-2009, 20:06
qualcosa di semiimplementato?

PGI-Bis
15-07-2009, 21:58
in java c'è jinput. Ad esempio:

import net.java.games.input.*;
import static net.java.games.input.Component.Identifier.*;

public class Main {

public static void main(String[] args) {
ControllerEnvironment env = ControllerEnvironment.getDefaultEnvironment();
Keyboard kb = null;
for(Controller c : env.getControllers()) {
if(c instanceof Keyboard) {
kb = (Keyboard)c;
}
}
final Keyboard keyboard = kb;
Component[] comps = keyboard.getComponents();
Component aKey = comps[37];
Event event = new Event();
for(int i = 0; i < 100; i++) {
keyboard.poll();
if(keyboard.isKeyDown(Key.A)) {
System.out.println("bingo!!!");
}
try {
Thread.sleep(100);
} catch(InterruptedException ex) {return;}
}
}
}

Questo stampa un "bingo!!!" quando si preme A, usando il DirectInputEnvironmentPlugin in Windows.

jsdesign
17-07-2009, 00:24
serve la libreria jinput, io l'ho caricata ma mi lancia un eccezione di tipo unsatisfied ecc.... che faccio??

PGI-Bis
17-07-2009, 15:29
ci sono delle librerie dinamiche insieme al jar (sia le dll windows che le so unix). Quelle librerie vanno in uno dei percorsi che java usa per caricare le librerie native. Tra questi percorsi è incluso quello di esecuzione del programma (la cartella da cui lanci il tuo programma).

Se usi Netbeans basta metterle nella cartella del progetto tramite la scheda files, per Eclipse non saprei ma immagino che sia una cosa simile.

Tra l'altro il codice che ho incollato ha un sacco di roba che non serve - ho spudoratamente riciclato un vecchio esperimento. La parte significativa è:

package keytest;

import net.java.games.input.Component.Identifier.Key;
import net.java.games.input.Controller;
import net.java.games.input.ControllerEnvironment;
import net.java.games.input.Keyboard;

public class Main {

public static void main(String[] args) {
ControllerEnvironment env = ControllerEnvironment.getDefaultEnvironment();
Keyboard kb = null;
for (Controller c : env.getControllers()) {
if (c instanceof Keyboard) {
kb = (Keyboard) c;
}
}
for (int i = 0; i < 100; i++) {
kb.poll();
if (kb.isKeyDown(Key.A)) {
System.out.println("bingo!!!");
}
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
return;
}
}
}
}

Aggiungi alle opzioni di esecuzione:

-Djinput.plugins=net.java.games.input.DirectInputEnvironmentPlugin -Djinput.useDefaultPlugin=false

altrimenti è necessario che il programma abbia almeno una finestra attiva.

jsdesign
18-07-2009, 11:04
come aggiungo alle opzioni di esecuzione quei parametri?
le dll le ho messe in src, uso netbeans, ok?

PGI-Bis
18-07-2009, 14:33
in netbeans -> click col destro sul progetto -> properties-> run -> VM Options

jsdesign
18-07-2009, 20:11
Loading: net.java.games.input.DirectInputEnvironmentPlugin
java.lang.UnsatisfiedLinkError: no jinput-dx8 in java.library.path
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1709)
at java.lang.Runtime.loadLibrary0(Runtime.java:823)
at java.lang.System.loadLibrary(System.java:1028)
at net.java.games.input.DirectInputEnvironmentPlugin$1.run(DirectInputEnvironmentPlugin.java:75)
at java.security.AccessController.doPrivileged(Native Method)
at net.java.games.input.DirectInputEnvironmentPlugin.loadLibrary(DirectInputEnvironmentPlugin.java:67)
net.java.games.input.DirectInputEnvironmentPlugin is not supported
at net.java.games.input.DirectInputEnvironmentPlugin.<clinit>(DirectInputEnvironmentPlugin.java:107)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:169)
at net.java.games.input.DefaultControllerEnvironment.getControllers(DefaultControllerEnvironment.java:156)
at keylogger.Main.main(Main.java:23)
Exception in thread "main" java.lang.NullPointerException
at keylogger.Main.main(Main.java:29)
Java Result: 1


Ho aggiunto le opzioni che dicevi e le dll necessarie in src... perchè continua a dare problemi??

PGI-Bis
18-07-2009, 20:17
dice che manca la dll jinput-dx8

Le dll che devi avere nella cartella del progetto sono

jinput-dx8.dll
jinput-raw.dll
jinput-wintab.dll

Se hai un os a 64bit sono:

jinput-dx8_64.dll
jinput-raw_64.dll
jinput-wintab.dll

Non devi metterle nella cartella src ma nella cartella del progetto. Se il progetto si chiama "bongo" allora hai nel filesystem una cartella bongo che contiene poi tutte le altre cartelle (build, src, nbproject, test). Devi mettere le dll in "bongo".

jsdesign
19-07-2009, 17:59
Grazie mille ora funziona:)
Mi chiedevo... siccome la libreria la conta come esterna quando creo il jar; non si può fare qualcosa in modo che la includo in un unico pacchetto... o magari di questa jinput esistono i sorgenti??

GRAZIE

PGI-Bis
19-07-2009, 18:06
Se la licenza lo permette (bisognerebbe guardare il sito) puoi sempre provare a "spacchettare il jar" - che è poi uno zip - e aggiungere il suo contenuto al tuo jar. Le liberie dinamiche tuttavia devono restare fuori dal Jar. Si possono distribuire dentro al jar ma bisogna comunque estrarle nel momento in cui il programma è lanciato (nella cartella user.dir).

jsdesign
19-07-2009, 22:30
a questo punto ripiego su c#... c'è qualcosa di pronto?