Unix - Terminal - 2 Tasten gleichzeitig einlesen / KeyEvent abfragen
-
Hallo Leute
als erstes muss ich sagen, dass ich kein C++ Programmierer bin und ich für jede konstruktive Kritik und Verbesserungsvorschläge (was unter anderem auch Konventionen angeht) sehr dankbar bin.
Nun zu meinem Problem:
Ich möchte eine Steuerung für ein Modellauto programmieren. Und zwar soll das Auto z.B. auf Tastendruck A nach links steuern und auf gleichzeitigem Tastendruck W auch geradeaus fahren (also nach vorne links fahren).So wie das jetzt programmiert ist, funktioniert das nicht, hier ist es ein "entweder oder" und selbst das ist nicht richtig durchdacht. Glaub die Geschichte mit den Threads ist auch nicht ganz richtig...
Im Endeffekt brauche ich nur einen KeyEventHandler den ich fragen kann ob eine Taste gedrückt ist oder nicht und das muss simultan für 2 Tasten funktionieren.
Ich nutze eclipse unter Unix. (getAsyncKeyState fällt also weg)
Für jeden nützlichen Rat bin ich dankbar
#include <iostream> #include <stdio.h> #include <boost/thread.hpp> #include <termios.h> using namespace std; int myGetch(); int myGetch() { static int ch = -1, fd = 0; struct termios recent, old; fd = fileno(stdin); tcgetattr(fd, &old); recent = old; recent.c_lflag &= ~(ICANON | ECHO); tcsetattr(fd, TCSANOW, &recent); ch = getchar(); tcsetattr(fd, TCSANOW, &old); return ch; } void drive(int direction) { if (direction == 1) { // vorwärtsfahren an cout << "vorwärts.. an" << endl; while (myGetch() == 'w') { cout << "vorwärts..." << endl; } // vorwärtsfahren aus cout << "vorwärts.. aus" << endl; } else if (direction == 2) { // linksfahren an cout << "links.. an" << endl; while (myGetch() == 'a') { cout << "links..." << endl; } // linksfahren aus cout << "links.. aus" << endl; } else if (direction == 3) { // rückwärtsfahren an cout << "rückwärts.. an" << endl; while (myGetch() == 's') { cout << "rückwärts..." << endl; } // rückwärtsfahren aus cout << "rückwärts.. aus" << endl; } else if (direction == 4) { // rechtsfahren an cout << "rechts.. an" << endl; while (myGetch() == 'd') { cout << "rechts..." << endl; } // rechtsfahren aus cout << "rechts.. aus" << endl; } } int main(int argc, char * argv[]) { char ch; while (1) { ch = myGetch(); if (ch == 'w') { boost::thread t1(&drive, 1); } else if (ch == 'a') { boost::thread t2(&drive, 2); } else if (ch == 's') { boost::thread t3(&drive, 3); } else if (ch == 'd') { boost::thread t4(&drive, 4); } else if (ch == 'b') { // Bremsenmodus wechseln cout << "switch brakes mode" << endl; } else if (ch == 'l') { // LEDModus wechseln cout << "switch led mode" << endl; } } return 0; }
-
Unter Unix könntest du ioctl oder die Keystate Funktion eines Frameworks wie XLib verwenden, um in deiner Schleife die Tasten abzufragen.
-
Youka schrieb:
Unter Unix könntest du ioctl ... verwenden, um in deiner Schleife die Tasten abzufragen.
FILE *kbd; glob_t kbddev; // Glob structure for keyboard devices glob("/dev/input/by-path/*-kbd", 0, 0, &kbddev); // Glob select all keyboards for ( i = 0; i < kbddev.gl_pathc ; i++ ) // Loop through all the keyboard devices { kbd = fopen(kbddev.gl_pathv[i], "r"); char key_map[KEY_MAX/8 + 1]; // Create a byte array the size of the number of keys memset(key_map, 0, sizeof(key_map)); // Initate the array to zero's ioctl(fileno(kbd), EVIOCGKEY(sizeof(key_map)), key_map); // Fill the keymap with the current keyboard state int keyb = key_map[key/8]; // The key we want (and the seven others arround it) int mask = 1 << (key % 8); // Put a one in the same column as the key state will be in if (keyb & mask) // If they key is pressed { return 0; } fclose(kbd); }
Auf den ersten Blick sieht das so unglaublich hässlich aus
-
Verschieb´ das
fclose(kbd);
besser in Zeile 13.
-
Dieser Thread wurde von Moderator/in SeppJ aus dem Forum C++ (auch C++0x und C++11) in das Forum Linux/Unix verschoben.
Im Zweifelsfall bitte auch folgende Hinweise beachten:
C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?Dieses Posting wurde automatisch erzeugt.
-
crispin kenton schrieb:
Auf den ersten Blick sieht das so unglaublich hässlich aus
Du kannst es dir auch als Funktion in eine Datei für Hilfsmittel packen.
#include <glob.h> #include <linux/input.h> #include <stdio.h> char is_key_pressed(int key){ // Data glob_t kbd_devs; int i; FILE *kbd; char key_map[KEY_MAX >> 3 + 1]; // Iterate through keyboards glob("/dev/input/by-path/*-kbd", 0, 0, &kbd_devs); for (i = 0; i < kbd_devs.gl_pathc; ++i){ // Get keyboard state kbd = fopen(kbd_devs.gl_pathv[i], "r"); ioctl(fileno(kbd), EVIOCGKEY(sizeof(key_map)), key_map); fclose(kbd); // Key is pressed? if(key_map[key >> 3] & 1 << key % 8) return 1; } return 0; }
-
Vielen Dank schonmal soweit, ich denke, dass ioctl das ist was ich benötige. Xlib fragt den KeyState ja aus X ab, da weiß ich nicht ob das funktioniert mit dem was ich vor habe.
Das Programm läuft auf einem RaspBerry Pi und wird via ssh aufgerufen, da wüsste ich nicht ob er die Keys vom Pi oder vom Remotegerät überwacht.
Der erste Code hat nicht funktioniert, wenn ich den ausgeführt habe bekam ich einen Segmentation fault (core dumped) in der Konsole.
Ich habe den letzten geposteten Code mal in eine C Datei kopiert und etwas dazu gebaut.
#include <stdio.h> //FILE #include <linux/input.h> //KEY_MAX #include <glob.h> //glob char is_key_pressed(int key); int main() { while (1) { printf("Characters: %c \n", is_key_pressed('w')); } return 0; } char is_key_pressed(int key){ // Data glob_t kbd_devs; int i; FILE *kbd; char key_map[KEY_MAX >> 3 + 1]; // Iterate through keyboards glob("/dev/input/by-path/*-kbd", 0, 0, &kbd_devs); for (i = 0; i < kbd_devs.gl_pathc; ++i){ // Get keyboard state kbd = fopen(kbd_devs.gl_pathv[i], "r"); ioctl(fileno(kbd), EVIOCGKEY(sizeof(key_map)), key_map); fclose(kbd); // Key is pressed? if(key_map[key >> 3] & 1 << key % 8) return 1; } return 0; }
Bei diesem Code ist es das gleiche Problem:
Segmentation fault (core dumped)Weiß jemand Rat?
@Youka, das mit dem Auslagern hatte ich eh vor, ich werde diese C Datei dann in meine vorhandene CPP Datei includen und die Funktion dort aufrufen. Die main hier ist jetzt nur zum Testen.
-
Nachtrag:
Wenn ich das Programm mit sudo starte funktioniert es, klasse!
Allerdings macht es nicht das was es soll (bzw. was ich möchte) oder?
Es sollte mir "Characters: 1" ausgeben wenn ich das 'w' gedrückt halte und sonst "Characters: 0".
Wenn das funktionieren sollte, wäre der nächste Schritt die Funktion in eigenen Threads nebenläufig aufzurufen. Ich hoffe, dass das Funktioniert und er mir bei 2 verschiedenen Tasten gleichzeitig sagen kann ob sie gedrückt sind oder nicht.
-
Versuch es mal mit
KEY_W
statt'w'
.(Die Segfaults kommen, wenn man Rückgabewerte nicht prüft. In dem Fall speziell von
fopen
.)Da aber über alle Eingabegeräte iteriert wird, glaube ich nicht, dass es über ssh funktioniert. (Das Programm dürfte so nur Tastendrücke auf dem Rechner registrieren, auf dem es läuft.)
-
Mit KEY_W hat es funktioniert, habe das nun auch nebenläufig programmiert und es klappt alles wunderbar, dankeschön
Allerdings liegt mir das mit dem SSH schwer im Magen falls das nicht funktionieren sollte. Weiß das jemand sicher, dass das nicht funktioniert? Bzw. hat dann ggf. auch schon einen Lösungsvorschlag parat?
Ich werde die SSH Verbindung Dienstag Abend erst testen können denke ich.
Dass das über SSH laufen soll ist unabdingbar.
(War auch mein Fehler, das nicht von Anfang an geschrieben zu haben)Idee:
Das System auf dem das Programm läuft muss dann auf der Konsole (statt in diesen keyboardfiles) 2 Tastenschläge gleichzeitig erkennen können, ohne dass der Eingabestream blockiert wird. Wenn man normal eine Taste länger drückt auf der Konsole und dann noch eine, wird das Schreiben der ersten Taste beendet und die zweite Taste wird gedruckt... Es sollten die Tasten "im Wechsel" gedruckt werden.Allerdings glaube ich nicht so recht daran, dass das dann kann und macht was ich möchte. Da dies ja nur eine nachgebaute schlechtere Keystate Funktion wäre und bei einer Abfrage ob das 'w' gedrückt ist die Erfolgschance bei 50% liegt wenn auch gleichzeitig das 'd' gedrückt wird.
Ich hoffe ich habe meine Bedenken und mein Problem verständlich erläutert.