PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : C/C++ lib für Sonderzeichenkombinationen (F1-12, +shift, alt, ctrl) als Scancode



HaWe
04.04.2016, 21:08
hallo,
wer kennt eine C/C++ lib für Sonderzeichenkombinationen (F1-12, +shift, alt, ctrl), um sie als Art "scancode" von der Tastatur zu lesen?

es soll so ähnlich funktionieren wie mit getch() / getchar(), nur dass nicht ein ASCII Zeichen gelesen werden soll, sondern ein einzigarteiger "Scancode" für die Tastenkombi (also keine Folge von einzelnen chars), z.B.
64031 für F1
65031 für ctrl+F1
66031 für shift+F1
...
usw,
praktischerweise auch
27 für ESC und
65 für 'A' etc,

um sie einfach abfragen und behandeln zu können, wie hier im Pseudocode:



int32_t i=getkeycode();

if(i==27) {...} // ESC: return NULL, terminate program (quit)
else
if(i==64031) {...} // F1: switch openVG window 1
else
if(i==64032) {...} // F2: switch openVG window 2
else
if(i==64033) {...} // F3: switch openVG window 3
else
if(i==63001) {...} // ctrl + : zoom in
else
if(i==63002) {...} // ctrl - : zoom out
else
if(i==64111) {...} // cur_up: move slow forward
else
if(i==64112) {...} // cur_dn: move slow reverse
else
if(i==66111) {...} // shift+cur_up: move fast forward
else
if(i==66112) {...} // shift+cur_dn: move fast reverse
else
if(i==64113) {...} // cur_left: turn slow left
else
if(i==64114) {...} // cur_right: turn slow right
else
if(i==66113) {...} // shift+cur_left: turn strong left
else
if(i==66114) {...} // shift+cur_right: turn strong right
else
if(i==65113) {...} // ctrl+cur_left: spin left
else
if(i==65114) {...} // ctrl+cur_right: spin right
else
if(i==64120) {...} // end: stop move, coast
else
if(i==65120) {...} // ctrl+end: stop move, brake
...



Man bräuchte dazu allerdings auch eine Tabelle als Übersicht, um die Zeichen nachschlagen zu können:



key plain shift ctrl alt shift+ctrl ctrl+alt shift+alt shift+ctrl+alt
Home
Insert
Delete
End
Pgup
PgDn
F1
F2
F3
...
cur_up
cur_dn
cur_left
cur_right
...
A
B
C
...
a
b
c
...



wo gibt's sowas, nach 40 Jahren C und Unix/Linux ?

Mxt
05.04.2016, 07:14
Hallo,

mit Tastaturen unter Linux kenne ich mich nicht so aus. Du musst die Linux Doku finden, die die entsprechnenden Dinge beschreibt, wie hier das Windows Gegenstück:
https://msdn.microsoft.com/en-us/library/windows/desktop/ms645530%28v=vs.85%29.aspx

Peter(TOO)
05.04.2016, 08:44
Hallo,

Die Frage ist jetzt auf welcher Ebene du ansetzen willst?

Ganz früher haben Tastaturcontroller direkt den ASCII-Code der Tasten geliefert. Das brauchte dann aber für jede Tastenanordnung (Sprache) einen anderen Controller. STRG und SHFT wurden nur intern verwendet. An Computer wurden Terminals angeschlossen, Einheiten aus Tastatur und Bildschirm. Für die Codierung von z.B. Coursor-Tasten, welche in ASCII nicht vorgesehen sind, hatte jeder Terminal-Hersteller eine eigene Lösung. Wie auch für die Steuerbefehle ans Terminal.
Sehr verbreitet waren die DEC VT100 Terminals, woraus sich dann auch eine ANSI-Norm für Terminals entwickelte.


IBM hat dann beim PC die Tasten einfach durchnummeriert. Jede Taste setzt dann ein Signal beim Drücken und eines beim loslassen ab (Scancode).
Im PC war dann ein Tastaturtreiber, welcher aus den Scancodes dann die entsprechenden ASCII-Werte erzeugt und die speziellen Tasten wie STRG, SHFT usw. auswertet.
Für Sondertasten gibt es keine definierte ASCII-Codes, also wurden für sie zwei Zeichen erzeugt.


wo gibt's sowas, nach 40 Jahren C und Unix/Linux ?
Da liegt halt der Hund begraben!
Den IBM-PC gibt es erst seit 35 Jahren und Unix kam erst später auf den IBM-PC.
Unix war zuerst auf Grossrechnern vorhanden, weshalb es unter Unix/Linux deshalb heute immer noch eine Menge Treiber für Terminals gibt oder Treiber welche eines der Terminals simulieren.

MfG Peter(TOO)

Mxt
05.04.2016, 09:21
Ich glaube mit Terminal hat Peter das richtige Stichwort genannt.

getch() und Verwandte setzen wahrscheinlich auf den Terminal-Funktionen auf. Man muss also mindestens auf diese Ebene herunter. Es gibt da auch einen raw Modus, möglicherweise liefert der das gesuchte. Das alles ist verwandt mit den Funktionen zur UART-Programmierung, das ist für Unix alles das selbe, genau wie Peter schon sagt. Früher hingen alle Textterminals an seriellen Schnittstellen.

Noch tiefer kommt man wohl nur mit raw input, das geht irgendwie mit Sachen aus den /dev/... Verzeichnissen. Hab ich noch nie gemacht.

HaWe
05.04.2016, 10:49
im Prinzip ist mir die Ebene egal, ich lese die Tastatur in einer Art pthread "keyboard watcher task" - bisher wird nur die Taste ESC überwacht für einen program abort, aber nun sollen auch andere Tasten überwacht werden, insbesondere Sondertasten:


*arrrgs* wieder kein Code Tag!!!


void* thread1Go(void *) // medium priority: keyboard + heartbeat monitoring
{
int c; // keyboard key

while(_TASKS_ACTIVE_) {
c=0;
if (kbhit()) { // <<<<<<<<<<<<<<<<<<
c = getchar();
if( c==27 ) { // ESC to quit program
printf("\n\n ESC pressed - program terminated by user \n");
_TASKS_ACTIVE_=0; // semaphore to stop all tasks
printf("\n wait for tasks to join main()... \n\n");
return NULL;
}
}

//... ***SNIP***

delay(50);
}
return NULL;
}

kbhit habe ich als conio-mimic hierraus:



#ifndef RPICONIO_H
#define RPICONIO_H


#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <termio.h>
#include <unistd.h>

bool kbhit(void)
{
struct termios original;
tcgetattr(STDIN_FILENO, &original);

struct termios term;
memcpy(&term, &original, sizeof(term));

term.c_lflag &= ~ICANON;
tcsetattr(STDIN_FILENO, TCSANOW, &term);

int characters_buffered = 0;
ioctl(STDIN_FILENO, FIONREAD, &characters_buffered);

tcsetattr(STDIN_FILENO, TCSANOW, &original);

bool pressed = (characters_buffered != 0);

return pressed;
}


//... ***SNIP***


#endif




über wiederholtes getchar() Auslesen ginge es zwar prinzipiell, aber F1 liefert 3 Zechen im keyb-Puffer (27,79,80) und shift-F1 wohl sogar 6, was ein Unding wäre, das über eine extra state machine verarbeiten zu wollen - für alle 102x7 möglichen key+modifier-Kombis.
Also wird eine Art "Scancode" gebraucht, so wie es das schon früher bei Turbo Pascal und Turbo C optional, zusätzlich zu readkey oder getchar gab.

Mxt
05.04.2016, 12:08
Also wird eine Art "Scancode" gebraucht,

Die Werte der Codes siehst du in /usr/include/linux/input.h

Wahrscheinlich arbeitet man damit, in dem man /dev/input/irgendwas öffnet. Habe ich, wie gesagt, noch nie probiert. Meine Raspis haben nichtmal eine Tastatur.

stecdose
05.04.2016, 12:58
über wiederholtes getchar() Auslesen ginge es zwar prinzipiell, aber F1 liefert 3 Zechen im keyb-Puffer (27,79,80) und shift-F1 wohl sogar 6, was ein Unding wäre, das über eine extra state machine verarbeiten zu wollen - für alle 102x7 möglichen key+modifier-Kombis.

Shift-F1 ist eine Kombination, ich denke bei 3 für eine Einzelne ist Schluss. Kombinationen muss man immer nach dem Puffer verarbeiten. Ausser man nutzt eine library wie ncurses, welche ich persönlich gern nutze. Sehr einfache Struktur und mit ein paar Zeilen Ergebnisse.

So aufwendig ist das ohne eine Library auch nicht... Pseudocode:


uint8_t c, len;
int ret;

c = get_scancode();
len = lookup_len(c); // tabelle/array mit möglichen längen (1-3)
array_rcv[0] = c; // erstes/einzelnes zeichen speichern
if(len > 1) {
ret = get_more(array[1], array[2], len); // empfange len mehr zeichen, return = empfangene zeichen
// etwas tun
}

HaWe
05.04.2016, 14:03
ncurses nutzt eine eigenes Terminal-Window mit eigener print-Funktion
mvaddstr
und mit einem ziemlichen Initialisierungs-Overhead

initscr();
raw();
noecho();
keypad(stdscr, TRUE);

und mit Eigenschaften, die std-printf()-Ausgaben "verzerren", so wird z.B. nach \n kein carriage return ausgeführt. Mehrmals einen string mit \n ausgeben lässt ihn daher mit Treppenstufen-Versatz in die nächste Zeile nach rechts rutschen, nicht untereinander ab Anfang jeder Zeile.
Außerdem kann ncurses - zumindest per Testcode - z.B. kein shift+F1 anzeigen. Von daher ist also einerseits nicht mehr standardconform, und zum anderen auch noch von sehr eingeschränktem Nutzungsumfang.

für ncurses hatte ich mal diesen testcode ausprobiert:



#include <ncurses.h>

int
main( void )
{
/* Initialize ncurses */
initscr();
const char *str;
int ch;

/* Switch of echoing and enable keypad */
raw();
noecho();
keypad(stdscr, TRUE);
mvaddstr( LINES-1, 0, "Press keys, 'q' to quit" );

/* Loop until user hits 'q' to quit */
while( (ch = getch()) != 'q' )
{
switch( ch )
{
case KEY_F(1):
str = "F1 key";
break;
case KEY_F(2):
str = "F2 key";
break;
case KEY_F(3):
str = "F3 key";
break;
case KEY_F(4):
str = "F4 key";
break;

case 'a' ... 'z':
str = "Lower case letter";
break;
case 'A' ... 'Z':
str = "Upper case letter";
break;
case '0' ... '9':
str = "Digit";
break;

case KEY_UP:
str = "KEY_UP";
break;

case KEY_DOWN:
str = "KEY_DOWN";
break;
case KEY_LEFT:
str = "KEY_LEFT";
break;
case KEY_RIGHT:
str = "KEY_RIGHT";
break;
case KEY_HOME:
str = "KEY_HOME";
break;
case KEY_END:
str = "KEY_END";
}

//mvaddstr( LINES-1, 0, str );
printf("%s\n", str);
clrtoeol();
}

/* Clean up after ourselves */
endwin();

return 0;
}





Einen (wünschenswerten) Pseudocode hatte ich ja bereits selber skizziert, zu Erläuterungszwecken.

Was ich aber jetzt brauche, ist kein Pseudocode, sondern echten Code, zusammen mit einer Liste aller möglichen denkbaren modifier-Kombis, damit man nicht das Rad neu erfinden muss.



**arrrgs** und schon wieder keine Code Tags hier vorhanden...


key plain shift ctrl alt shift+ctrl ctrl+alt shift+alt shift+ctrl+alt
Home
Insert
Delete
End
Pgup
PgDn
F1
F2
F3
...
cur_up
cur_dn
cur_left
cur_right
...
A
B
C
...


"Das Rad nicht neu erfinden" gilt übrigens auch für meine gesuchte Funktion, ich denke, das müsste es nach 40 Jahren UNIX/Linux sicher schon längst geben.




- - - Aktualisiert - - -

pps,
für BIOS/MSDOS etc gabs das ja, gelistet z.B. hier
http://flint.cs.yale.edu/cs422/doc/art-of-asm/pdf/APNDXC.PDF
(Appendices Page 1353)

so etwas bräuchte ich nun für Linux als Art "getscancode()" etc.

stecdose
05.04.2016, 14:20
Hmm... Die Modifier-Tasten sind eben nur Modifier und ich kenne da keinen anderen Weg. Vielleicht hilft dir das ja weiter, um alles Steuerzeichen direkt zu bekommen:

https://www.daniweb.com/programming/software-development/threads/147540/how-can-we-get-scan-code-in-c-using-gcc-compiler

- - - Aktualisiert - - -

Hmm... Die Modifier-Tasten sind eben nur Modifier und ich kenne da keinen anderen Weg. Vielleicht hilft dir das ja weiter, um alles Steuerzeichen direkt zu bekommen:

https://www.daniweb.com/programming/software-development/threads/147540/how-can-we-get-scan-code-in-c-using-gcc-compiler

HaWe
05.04.2016, 14:42
das ist die oben von mir skizzierte kaskadierte Methode.
Für F1 muss man dazu hintereinander 27, dann 79, dann 80 auslesen, um es als F1 zu identifizieren, und für shift-F1 nochmal 3 chars zusätzlich.
Dann denk mal drüber nach, welche Verrenkungen nötig sind, um ein ctr+shift+F1 von einem ctr+shift+F2 oder einem alt+ctrl+F2 oder einem shift+ctrl+alt+F1 oder shift+ctrl+alt+F2 zu unterscheiden....

Das bringt doch wie gesagt nichts.
Was man bräuchte, wäre der Scancode, was eher dem gleichkommt, was die Keyboard-Hardware selber produziert, nicht das, was das Linux-Terminal daraus macht.

wie könnte man z.B.
/dev/input/*
auslesen?

Und wo findet man die zugehörige Tasten-Kombinationen-Tabelle?

stecdose
05.04.2016, 14:49
Die Doku dazu ist hauptsächlich als simpler Text verfügbar und auch nicht gerade Umfangreich, da das ohne lib kaum jemand benutzt.

https://www.kernel.org/doc/Documentation/input/

input und input-programming dürften interessant sein.

- - - Aktualisiert - - -

Die Doku dazu ist hauptsächlich als simpler Text verfügbar und auch nicht gerade Umfangreich, da das ohne lib kaum jemand benutzt.

https://www.kernel.org/doc/Documentation/input/

input und input-programming dürften interessant sein.

HaWe
05.04.2016, 15:35
nun, hmm, da bräuchte ich jetzt tatkräftige Hilfe von jemandem mit richtig viel Kenne, wie man das jetzt zur Problemlösung einsetzt ... :confused:

Peter(TOO)
05.04.2016, 16:16
Hallo,

und mit Eigenschaften, die std-printf()-Ausgaben "verzerren", so wird z.B. nach \n kein carriage return ausgeführt. Mehrmals einen string mit \n ausgeben lässt ihn daher mit Treppenstufen-Versatz in die nächste Zeile nach rechts rutschen, nicht untereinander ab Anfang jeder Zeile.
Eigentlich ist es genau umgekehrt! :-P

Normal ist, dass LF (LineFeed,"\n") eben NUR das Papier um eine Zeile weiter transportiert.
Entsprechend fährt CR (CarriageReturn, "\r") eben nur den Wagen nach links.
Das "\n" in CR LF übersetzt wird ist die "Verzerrung".

Aber bei Unix kann dies alles unter TTY eingestellt werden.

Früher brauchte man LF, BS usw. als Coursorsteuerung.
Zudem konnte man auf einem einfachen TTY mit CR Fettdruck erreichen, indem man den selben Text mehrmals übereinander gedruckt hat.

TTY ist das was man als Fernschreiber kennt. Bis so etwa Mitte der 70er war dies das normale Ein/Ausgabegerät für Computer. Drucker waren damals noch sehr Teuer. So 1978 brachte Centronics den Low Costdrucker 779 heraus, kostete damals um die CHF 4'800.-- und war ein A4-Hochformat 7-Nadeldrucker mit Stachelwalzen, ohne Unterlängen, und schaffte 40 Zeichen/s (eigentlich 80Z/s aber er druckte nur unidirektional und der Rücklauf war gleich schnell wie der Druck). Gaphik konnte der auch noch nicht drucken, nur ASCII-Zeichen.

Etwas später kam dann Epson mit dem RX 80 (CHF 1'800.--) als A4 Hoch und RX 100 (A4 quer).
Zur damaligen Zeit gab es eine billige Typenrad-Schreibmaschine, welche man einfach zu einem Drucker umbauen konnte. Allerdings war diese langsam und mechanisch nicht für hundertseitige Listings ausgelegt. Die lebten dann meistens nicht sehr lange.

MfG Peter(TOO)

HaWe
05.04.2016, 16:48
printf("\n") macht in C standardmäßig ein LF und ein CR, sowohl auf Arduinos als auch auf Raspi-LX-Terminals. Das ist der Standard.
Das ist aber nicht der Punkt, und deinen Historienwissenserguss in allen Ehren - es ist OT und hilft nicht bei der Problemlösung, denn es sind Scancodes gefragt, die man einfach aus einem C Programm heraus zur Sondertastenerkennung benutzen kann.
Selbst da aber versagt ncurses, unabhängig von dem Terminal-Anzeige-Problem, denn es erkennt ja kaum welche in Kombi mit den modifiern, und ich brauche und will auch gar kein extra-Terminal.

Auch geht es ja gar nicht um stdout, sondern stdin, denn daraus liest ja auch mein getchar (stdio.h) und mein tcgetattr aus termio.h.


wie nützt man nun also, alternativ, z.B.
https://www.kernel.org/doc/Documentation/input/
oder auch jeden anderen gangbaren und praktikablen Weg?

peterfido
05.04.2016, 18:27
Hallo,

man kann alle Tasten in C abfragen. Du kannst sogar auf die Numlock-Taste im Programm reagieren.

Anbei ein auf das Wesentliche reduzierte Testprogramm:
// Compile with: gcc -o /var/scripte/tastaturtest /var/scripte/tastaturtest.c -L/usr/local/lib -lpthread
//nice -1 /var/scripte/tastaturtest < /dev/input/event0 &

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <sys/sysinfo.h>
#include <time.h>
#include <linux/input.h>
#include <pthread.h>

int ziffer=0;
int beenden=2;
int zeit=0;
int shiftl=0;
int shiftr=0;
int strgl=0;
int strgr=0;
int capsl=0;


unsigned char runterfahren(){
system("halt");
}

unsigned char zahlentaste()
{
printf("Zahlentaste: %d\n", ziffer);
}

unsigned char tnull()
{
ziffer=0;
zahlentaste();
}

unsigned char teins()
{
ziffer=1;
zahlentaste();

}

unsigned char tzwei()
{
ziffer=2;
zahlentaste();

}
unsigned char tdrei()
{
ziffer=3;
zahlentaste();

}
unsigned char tvier()
{
ziffer=4;
zahlentaste();

}
unsigned char tfuenf()
{
ziffer=5;
zahlentaste();

}
unsigned char tsechs()
{
ziffer=6;
zahlentaste();

}
unsigned char tsieben()
{
ziffer=7;
zahlentaste();

}
unsigned char tacht()
{
ziffer=8;
zahlentaste();

}
unsigned char tneun()
{
ziffer=9;
zahlentaste();

}
unsigned char tdel()
{
printf("Delete\n");
}

unsigned char tnum()
{
printf("Numlock\n");
}

unsigned char tenter()
{ printf("Enter\n");

}
unsigned char tplus()
{
printf("Plus\n");
}
unsigned char tminus()
{
printf("Minus\n");
}
unsigned char tmal()
{
printf("Mal\n");
}

unsigned char tgeteilt()
{
printf("Geteilt\n");
}

unsigned char tbs()
{
printf("Backspace\n");
}

unsigned char tq()
{
beenden=1;
}

unsigned char status()
{
printf("SHIFTL: %d, SHIFTR: %d, STRGL: %d, STRGR: %d\n",shiftl,shiftr,strgl,strgr);
}

unsigned char tf1()
{
if(shiftl>=1 || shiftr>=1){
printf("SHIFT + F1\n");
}else{
printf("F1\n");
}
}

void tastatur(void *name)
{
struct input_event event;
while ((read(0, &event, sizeof(event)) > 0) && (beenden==2) ) {
if (event.type != EV_KEY) continue;
// if (event.value != EV_KEY) continue; //Nur Tastendrücke erkennen, kein Loslassen. 0=losgelassen, 1=gedrückt, 2=Wiederholung(lang gedrückt)
printf("Typ: %d; Wert: %d; Code: %d\n",event.type,event.value,event.code);

switch (event.code) {
case 42: shiftl=event.value; break;
case 54: shiftr=event.value; break;
case 29: strgl=event.value; break;
case 97: strgr=event.value; break;
case 16: tq(); break; //q
case 82: tnull(); break; //0
case 79: teins(); break; //1
case 80: tzwei(); break; //2
case 81: tdrei(); break; //3
case 75: tvier(); break; //4
case 76: tfuenf(); break; //5
case 77: tsechs(); break; //6
case 71: tsieben(); break; //7
case 72: tacht(); break; //8
case 73: tneun(); break; //9
case 83: tdel(); break; //DEL
case 96: tenter(); break; //ENTER
case 78: tplus(); break; //+
case 74: tminus(); break; //-
case 55: tmal(); break; //*
case 98: tgeteilt(); break; ///
case 14: tbs(); break; //Backspace
case 69: tnum(); break; //NumLock
case 59: tf1(); break; //F1
case 31: status(); break; //s
}
}
beenden=0;
pthread_exit((void *) 0);
}

int main(int argc, char** argv)
{
pthread_t t1;
if(pthread_create(&t1, NULL, (void *)&tastatur, (void *)"Tastatur") != 0)
{
fprintf(stderr, "Fehler bei Tastatur......\n");
exit(0);
}

while (beenden > 0)
{ sleep(1);
zeit++;
if (zeit==30)
{
zeit=0;
printf("Wieder 30 Sekunden um.\n");
}
}
usleep(50);
printf("***********Ende************\n");
return 0;
}




Dieses Programm zeigt die Codes an, welche an einer AM RASPI ANGESCHLOSSENEN Tastatur gedrückten / losgelassenen Taste entspricht. Ein Beispiel mit F1 ohne und mit SHIFT habe ich eingefügt.

Das Programm wird mit q beendet. Als Startparameter muss das Device der Tastatur angegeben werden. Bei nur einer Tastatur reicht die zweite Zeile im Programmkopf zum Starten. Den Pfad (auch den zum zum kompilieren) natürlich anpassen.

HaWe
05.04.2016, 19:23
hallo,
super, das sieht ja sehr beeindruckend aus!
ich verwende Geany zum Kompilieren und habe da auch die compile- und Build- Parameter fest eingestellt.
Mit make etc. stehe ich auf dem kriegsfuß...

Ich verwende g++, nicht gcc, dürfte aber sicher kein Problem sein.

-pthread
hab ich schon drin

-L/usr/local/lib
muss sicher noch dazu

was sonst noch? den rest verstehe ich nämlich nicht.



bekomme sehr viele Fehlermeldungen

printf not declared => #include <stdio.h> eingefügt.
system not declared => ???
exit not declared => ???

int main(int argc, char** argv) geändert in
int main()

die Zeile mit if( pthread_create....) hat einen dicken Fehler
invalid conversion from void* to void*(*)(void*)
if kann man sicher doch ganz weglassen!!
Ich initialisiere immer nur per
pthread_create(&threadID, NULL, threadName, NULL);


Startparameter kann ich nicht eingeben!

peterfido
05.04.2016, 19:35
Hallo,

die Includes könnten mehr sein als notwendig sind. Das frisst aber kein Brot und in meinem Programm, wo ich das meiste rauskopiert habe, brauche ich diese. Geany sagt mir leider gar nichts. Ich kompiliere mit gcc. Der Aufruf dafür steht ja in der ersten Zeile.

Dann kommen die Variablen für die 'Sondertasten'. Wobei ich für Capslock kein Beispiel reinprogrammiert habe.

Je nach Tastencode (Scancode?) wird die dazugehörige Sub aufgerufen. Da braucht man natürlich nur die Codes der Tasten abfragen, die tatsächlich genutzt werden. Wird freie Texteingabe unterstützt, muss man das nachbauen.

Wird q gedrückt, wird zuerst der zweite Thread beendet und dann 50 Mikrosekunden später das Hauptprogramm. Schmiert das ab, dann bleibt der Thread am Leben. Ist aber nicht tragisch. GCC beendet den beim nächsten kompilieren automatisch ;)

Die 30 Sekunden Wartezeit sollen nur demonstrieren, dass das Hauptprogramm weiterläuft, auch wenn keine Taste gedrückt wird.

Ansonsten wird hhalt bei jedem Tastendruck die zugehörige Sub aufgerufen. Das Hauptprogramm und der zweite Thread teilen sich die globalen Variablen.


Edit:
Bei mir kompiliert er fehlerfrei. Hast Du gcc? Ich starte mal den Raspi und schaue, welche Pakete so drauf sind.

HaWe
05.04.2016, 19:38
nein, g++
und Startparameter kann ich nicht eingeben!
das Device der Tastatur kenne ich nicht

ich kann nicht ohne Geany compilieren. Kommandozeile kann ich nicht.

die includes stören mich nicht, aber bei dir fehlte stdio.h !

und wohl noch ein wenig mehr wegen system und exit

hier ist der Link zu meinem kompletten Programm, wo alles mit rein muss, nur zur Info:
http://www.mindstormsforum.de/viewtopic.php?f=78&t=8851&p=69043#p69089

Geany-Build-Parameter:

g++ -Wall -pthread -I/opt/vc/include -I/opt/vc/include/interface/vmcs_host/linux -I/opt/vc/include/interface/vcos/pthreads -o "%e" "%f" -lshapes -L/opt/vc/lib -lOpenVG -lEGL -lwiringPi -lrt

peterfido
05.04.2016, 19:56
Hallo,

stdio.h ist doch drin. Das erste Include gleich. (Siehe Post #15). Ob g++ auch C-Code kompilieren kann, weiß ich nicht. Ich progge nur in C (BASH / PHP mal außen vor) und alles per Kommandozeile. Wie gesagt, Geany habe ich nicht. Die installierten Pakete liste ich doch nicht auf. Es sind 846 Pakete.

Ich hänge mal meine fertig compilierte Datei hier an. Evtl. hilft sie Dir die Scancodes auszulesen. Die Datei ist unter Windows gepackt und muss auf den Raspi. Die Rechte auf 755 setzen und starten.

Wenn Du sie, wie im Quellcodekopf, mit nice startest, dann läuft sie mit geringerer Priorität. Das & am Ende bedeutet, dass das Programm im Hintergrund ausgeführt wird und die Konsole für andere Dinge genutzt werden kann.

Mehr kann ich da leider nicht für Dich tun. Die nächste Zeit bin ich erstmal hauptsächlich offline und komme auch nicht zum basteln.

Edit: Bei Dir steht zwar -pthread im Parameter, bei mir jedoch -lpthread. Also scheint es da Unterschiede zu geben. Probiere mal nur meinen Code zu compilieren, ohne ihn irgendwo einzubauen.

Edit 2:
int main(int argc, char** argv) ist für die angegebenen Parameter wichtig. Evtl. will Geany für die Fensteroberfläche kompilieren. Schau mal, ob man da auch ein Konsolenprogramm als Typ auswählen kann.

HaWe
05.04.2016, 20:05
war ein copy + paste Fehler mit den #includes.

Leider verstehe ich deinen Code nicht, es muss in meinen Task reinpassen, der läuft mit mittlerer prio, und darum geht es ja letztendlich:



void* thread1Go(void *) // medium priority: keyboard + heartbeat monitoring
{
int c; // keyboard key

while(_TASKS_ACTIVE_) {
c=0;
if (kbhit()) { // <<<<<<<<<<<<<<<<<<
c = getchar();

if( c==27 ) { // ESC to quit program
printf("\n\n ESC pressed - program terminated by user \n");
_TASKS_ACTIVE_=0; // semaphore to stop all tasks
printf("\n wait for tasks to join main()... \n\n");
return NULL;
}
else
if( ) { } //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< dein Code
}

//... ***SNIP***

delay(50);
}
return NULL;
}

Das auslesen müsste darin laufen, und die Art der gedrückten Taste könnte genau wie meine Meldungen bei ESC (analog per printf) in der Konsole angezeigt werden.

Konsolenprogramm soll nicht sein, ist meines auch nicht!
http://www.mindstormsforum.de/viewtopic.php?f=78&t=8851&p=69043#p69089




int main() {
char sbuf[128];
int err;

// **SNIP**

pthread_t thread0, thread1, thread2, thread3, thread4, thread5;
struct sched_param param;

pthread_create(&thread0, NULL, thread0Go, NULL); // lowest priority task: screen output
param.sched_priority = 10;
pthread_setschedparam(thread0, SCHED_RR, &param);

pthread_create(&thread1, NULL, thread1Go, NULL); // low priority: keyboard monitoring (stop program)
param.sched_priority = 20;
pthread_setschedparam(thread1, SCHED_RR, &param);

pthread_create(&thread2, NULL, thread2Go, NULL); // medium priority: motor control
param.sched_priority = 40;
pthread_setschedparam(thread2, SCHED_RR, &param);

pthread_create(&thread3, NULL, thread3Go, NULL); // highest priority: encoder reading
param.sched_priority = 80;
pthread_setschedparam(thread3, SCHED_FIFO, &param);

pthread_create(&thread4, NULL, thread4Go, NULL); // medium priority: UART comm <<< test !!
param.sched_priority = 40;
pthread_setschedparam(thread4, SCHED_FIFO, &param);

pthread_create(&thread5, NULL, thread5Go, NULL); // medium priority: navigation
param.sched_priority = 40;
pthread_setschedparam(thread1, SCHED_FIFO, &param);

//... **SNIP**

}

peterfido
05.04.2016, 20:39
Hallo,

das wird so nicht funktionieren. Dein getchar fragt nur ab, ob eine Taste gedrückt wurde. Bei Sondertasten wie SHIFT und Co. ist es auch wichtig zu wissen, wann diese wieder losgelassen wurden. Die linke SHIFT-Taste hat den Code 42 und die rechte 54.

Mein gcc kompiliert Deinen Code nicht. Da fehlen einige Libs, true sagt ihm nichts und bool kennt er als Typ nicht.

Bei mir wird '/dev/input/event0' als Eingabe angegeben. Der Thread tastatur liest ja ständig die Tastatur ein. Evtl. geht das unter g++ auch so. Mal testen.....
...
...
Ja, geht.
Folgender Code lässt sich mit g++ compilieren:

// Compile with: g++ -o /var/scripte/tastaturtest1 /var/scripte/tastaturtest1.c -L/usr/local/lib -lpthread
//nice -1 /var/scripte/tastaturtest1 < /dev/input/event0 &

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <sys/sysinfo.h>
#include <time.h>
#include <linux/input.h>
#include <pthread.h>

int ziffer=0;
int beenden=2;
int zeit=0;
int shiftl=0;
int shiftr=0;
int strgl=0;
int strgr=0;
int capsl=0;


unsigned char runterfahren(){
system("halt");
}

unsigned char zahlentaste()
{
printf("Zahlentaste: %d\n", ziffer);
}

unsigned char tnull()
{
ziffer=0;
zahlentaste();
}

unsigned char teins()
{
ziffer=1;
zahlentaste();

}

unsigned char tzwei()
{
ziffer=2;
zahlentaste();

}
unsigned char tdrei()
{
ziffer=3;
zahlentaste();

}
unsigned char tvier()
{
ziffer=4;
zahlentaste();

}
unsigned char tfuenf()
{
ziffer=5;
zahlentaste();

}
unsigned char tsechs()
{
ziffer=6;
zahlentaste();

}
unsigned char tsieben()
{
ziffer=7;
zahlentaste();

}
unsigned char tacht()
{
ziffer=8;
zahlentaste();

}
unsigned char tneun()
{
ziffer=9;
zahlentaste();

}
unsigned char tdel()
{
printf("Delete\n");
}

unsigned char tnum()
{
printf("Numlock\n");
}

unsigned char tenter()
{ printf("Enter\n");

}
unsigned char tplus()
{
printf("Plus\n");
}
unsigned char tminus()
{
printf("Minus\n");
}
unsigned char tmal()
{
printf("Mal\n");
}

unsigned char tgeteilt()
{
printf("Geteilt\n");
}

unsigned char tbs()
{
printf("Backspace\n");
}

unsigned char tq()
{
beenden=1;
}

unsigned char status()
{
printf("SHIFTL: %d, SHIFTR: %d, STRGL: %d, STRGR: %d\n",shiftl,shiftr,strgl,strgr);
}

unsigned char tf1()
{
if(shiftl>=1 || shiftr>=1){
printf("SHIFT + F1\n");
}else{
printf("F1\n");
}
}

void* tastatur(void *name)
{
struct input_event event;
while ((read(0, &event, sizeof(event)) > 0) && (beenden==2) ) {
if (event.type != EV_KEY) continue;
// if (event.value != EV_KEY) continue; //Nur Tastendrücke erkennen, kein Loslassen. 0=losgelassen, 1=gedrückt, 2=Wiederholung(lang gedrückt)
printf("Typ: %d; Wert: %d; Code: %d\n",event.type,event.value,event.code);

switch (event.code) {
case 42: shiftl=event.value; break;
case 54: shiftr=event.value; break;
case 29: strgl=event.value; break;
case 97: strgr=event.value; break;
case 16: tq(); break; //q
case 82: tnull(); break; //0
case 79: teins(); break; //1
case 80: tzwei(); break; //2
case 81: tdrei(); break; //3
case 75: tvier(); break; //4
case 76: tfuenf(); break; //5
case 77: tsechs(); break; //6
case 71: tsieben(); break; //7
case 72: tacht(); break; //8
case 73: tneun(); break; //9
case 83: tdel(); break; //DEL
case 96: tenter(); break; //ENTER
case 78: tplus(); break; //+
case 74: tminus(); break; //-
case 55: tmal(); break; //*
case 98: tgeteilt(); break; ///
case 14: tbs(); break; //Backspace
case 69: tnum(); break; //NumLock
case 59: tf1(); break; //F1
case 31: status(); break; //s
}
}
beenden=0;
pthread_exit((void *) 0);
}

int main()
{
pthread_t t1;
struct sched_param param;
pthread_create(&t1, NULL, tastatur, NULL); // low priority: keyboard monitoring (stop program)
param.sched_priority = 20;
pthread_setschedparam(t1, SCHED_RR, &param);

/* if(pthread_create(&t1, NULL, (void *)&tastatur, (void *)"Tastatur") != 0)
{
fprintf(stderr, "Fehler bei Tastatur......\n");
exit(0);
} */

while (beenden > 0)
{ sleep(1);
zeit++;
if (zeit==30)
{
zeit=0;
printf("Wieder 30 Sekunden um.\n");
}
}
usleep(50);
printf("***********Ende************\n");
return 0;
}


Vorgehensweise zum Testen wie oben.

HaWe
05.04.2016, 21:05
wenn ich jetzt kompiliere, zeigt er mit im LXTerminal an
(F7) ^[[18~

(F1) ^[OP
(shift+F1) ^[01;2P

dazwischen:
wieder 30 Sekunden um.

damit kann ich jetzt nicht so schrecklich viel anfangen...
(wie gesagt, von geany aus kompiliert, ohne Startparameter)

- - - Aktualisiert - - -

ich brauche eigentlich auch nur eine einfache Funktion
int getkeybscancode()

die mir eine nackte Zahl zurückliefert, den rest kann ich selber machen, wenn es für die Tastenkombis keine Tabellen gibt!

peterfido
05.04.2016, 22:02
Hallo,

es geht auch mit g++ und ohne Parameter.
Dafür musste ich die Vorgehensweise ändern.

Folgendes Beispiel sollte auch für Dich anpassbar sein. Der Scancode wird in der Variable keybscan abgelegt. Danach kannst Du eine Sub aufrufen oder die Variable in Deiner Mainloop pollen.


// Compile with: g++ -o /var/scripte/tastaturtest1 /var/scripte/tastaturtest1.c -L/usr/local/lib -lpthread
// /var/scripte/tastaturtest1
// 05.04.2016 - peterfido

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <sys/sysinfo.h>
#include <time.h>
#include <linux/input.h>
#include <pthread.h>
#include <errno.h>
#include <termios.h>
#include <signal.h>
#include <sys/types.h>
#include <fcntl.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/select.h>

int ziffer=0;
int beenden=2;
int zeit=0;
int shiftl=0;
int shiftr=0;
int shift =0;
int strgl=0;
int strgr=0;
int strg=0;
int capsl=0;
int alt=0;
int altgr=0;
int windows=0;
int kontext=0;
int keybscan=0;

void* thread1Go(void *)
{
int _TASKS_ACTIVE_=0;
struct input_event ev[64];
int fd, rd, value, size = sizeof (struct input_event);

if ((fd = open ("/dev/input/event0", O_RDONLY)) == -1){
printf ("Fehler mit Tastatur");
}else{
_TASKS_ACTIVE_=1;
}


struct input_event event;
while (_TASKS_ACTIVE_) {
/* _lastbeat_ = _heartbeat_;
_heartbeat_ = recvbuf[TERM];
if(abs(_heartbeat_ - _lastbeat_) ==0)
{ if(_missedbeats_ <998) _missedbeats_++; }
else _missedbeats_ = 0;
_REMOTE_OK_ = _missedbeats_ < 100;
delay(50);
*/
if ((rd = read (fd, ev, size * 64)) < size)
printf ("Fehler mit Tastatur");

if (ev[1].type != EV_KEY) continue;
if (ev[1].value==0){ //Taste losgelassen
switch (ev[1].code) {
case 42: shiftl=0; break;
case 54: shiftr=0; break;
case 29: strgl=0; break;
case 97: strgr=0; break;
case 56: alt=0; break;
case 125: windows=0; break;
case 100: altgr=0; break;
case 127: kontext=0; break;
}
}else{
if (ev[1].value==1){ //==1 für nur einen Druck ohne Wiederholung. >=1 für Erkennung von gedrückt gehaltener Taste
switch (ev[1].code) {
case 42: shiftl=1; break;
case 54: shiftr=1; break;
case 29: strgl=1; break;
case 97: strgr=1; break;
case 56: alt=1; break;
case 125: windows=1; break;
case 100: altgr=1; break;
case 127: kontext=1; break;
// Ab hier 'normale Tasten'

case 1: beenden=0; _TASKS_ACTIVE_=0; break; //ESC
default: keybscan=ev[1].code;// Scancode ablegen
printf("SHIFTL: %d, SHIFTR: %d, STRGL: %d, STRGR: %d; ",shiftl,shiftr,strgl,strgr);
printf("Typ: %d; Wert: %d; Code: %d\n",ev[1].type,ev[1].value,ev[1].code);
break;
}
}
}
}
beenden=0;
pthread_exit((void *) 0);
}

int main()
{
pthread_t thread1;
struct sched_param param;


if(pthread_create(&thread1, NULL, thread1Go, NULL) != 0)
{
fprintf(stderr, "Fehler bei Tastatur......\n");
exit(0);
}

param.sched_priority = 20;
pthread_setschedparam(thread1, SCHED_RR, &param);

beenden=1;

while (beenden > 0)
{ sleep(1);
zeit++;
if (zeit==30)
{
zeit=0;
printf("Wieder 30 Sekunden um.\n");
}
}
usleep(50);
printf("***********Ende************\n");
return 0;
}


Das Threading habe ich soweit von Dir übernommen. Einige Variablen fehlten mir, deren Deklaration muss dann in Deinem Programm wieder raus. Die Heartbeat-Geschichte habe ich einfach komplett auskommentiert. Die brauchst eigentlich nur wieder einkommentieren.

HaWe
06.04.2016, 08:22
super nett von dir, vielen Dank für deine Mühe!
War gestern schon zu Bette um die Zeit und bin jetzt auf Arbeit, werde es aber schnellstmöglichst testen, sowie ich wieder zuhause bin!
:)

peterfido
06.04.2016, 08:58
Hallo,

reiner Eigennutz. Diese Änderungen werde ich dann auch in meine Projekte bei der nächsten Überarbeitung mit übernehmen. Man kann so prima IR-Fernbedienungen nutzen, welche eine Tastatur / Maus emulieren. Für den Mauspart muss man dann noch ein weiteres Device auslesen. Probleme tauchen erst auf, wenn man keine Tastatur oder mehrere Tastaturen / Mäuse angeschlossen hat, was allerdings bei mir nicht vorkommt.

HaWe
06.04.2016, 10:20
so 'n Zufall, genau dafür nutze ichs eigentlich auch, prinzipiell zumindest ;)
ich habe dazu einfach eine Wireless Tastatur am Pi, entweder in normal groß im Testmodus (LogiLink) oder mini (Rii) wenn mobil unterwegs.
Zugegebenermaßen momentan aber beide USB-Dongles gleichzeitig eingestöpselt.... mal gucken, ich zieh die kleine dann erstmal raus....
Eine analoge Joystick-Konsole mit Arduino per BT ist dann aber das Standardwerkzeug zur Fernsteuerung.

HaWe
06.04.2016, 14:58
hallo, super, das Programm läuft!
es werden jetzt in 1 Zeile alle modifier plus die gedrückte "echte" Taste angezeigt.

Was hältst du davon, wenn man daraus einen eigenen Scancode generiert?

einfache Taste, Bereich 0...255, so wie jetzt bereits ausgegeben.

Modifier:
shift (links oder rechts, egal): = 1*1024
ctrl (links oder rechts, egal): = 2*1024
alt (links): = 4*1024
altgr (==ctrl+alt) : = 6*1024

diese werden dann zum Rest der gedrückten Tasten dazu addiert.

dann könnte man eine Funktion schreiben, die ausschließlich diesen Scancode berechnet und in einer globalen Variablen speichert.

Dazu könnte ich ein paar Konstanten entwerfen wie z.B.


#define F1 59
#define F2 60
#define F3 61
...

#define F1_shft 59+1024
#define F2_shft 60+1024
#define F3_shft 61+1024
...

#define F1_ctrl 59+2048
#define F2_ctrl 60+2048
#define F3_ctrl 61+2048
...

#define F1_alt 59+4092
#define F2_alt 60+4092
#define F3_alt 61+4092
...

usw



// global)
volatile int _kbscode_=0;


void getkbscancode() {
int modkeycode = 0;
//...
// hier jetzt deine Abfrage auf Druck ohne Wiederholung,
// dann modkeycode berechnen wie oben aus Summe aus
// shift (links oder rechts, egal): = 1*1024
// ctrl (links oder rechts, egal): = 2*1024
// alt (links oder rechts, egal): = 4*1024

_kbscode_ = ev[1].code + modkeycode // hier jetzt die Sondertasten als Vielfache von 1024 dazuaddieren
}

sodass man die kbstates abfragen kann per


if(_kbscode_==F1) {...}
else
//...
else
if(_kbscode_==F3_alt) {...}
...
else
if(_kbscode_==F1_shiftalt) {...}
//alternativ)
if(_kbscode_==F1 +1024+4048) {...}


usw...

Die Frage wäre, ob man dann einen eigenen Task dafür spendiert, der ausschließlich void getkbscancode() wiederholt aufruft und so automatisch als keypress-Watcher funktioniert.
Alternativ könnte man diese Funktion auch in jedem anderen, bereits laufenden Task aufrufen, quasi als zusätzliche Unterfunktion.

zwischenstadium:



// Compile with: g++ -o /var/scripte/tastaturtest1 /var/scripte/tastaturtest1.c -L/usr/local/lib -lpthread
// /var/scripte/tastaturtest1
// 05.04.2016 - peterfido
// variant 06.04.2016 - hawe

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <sys/sysinfo.h>
#include <time.h>
#include <linux/input.h>
#include <pthread.h>
#include <errno.h>
#include <termios.h>
#include <signal.h>
#include <sys/types.h>
#include <fcntl.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/select.h>

int ziffer=0;
int beenden=2;
int zeit=0;
int shiftl=0;
int shiftr=0;
int shift =0;
int strgl=0;
int strgr=0;
int strg=0;
int capsl=0;
int alt=0;
int altgr=0;
int windows=0;
int kontext=0;
int keybscan=0;

int modscode;
volatile int _kbscode_ ;


void* thread1Go(void *)
{
int _TASKS_ACTIVE_=0;
struct input_event ev[64];
int fd, rd, value, size = sizeof (struct input_event);

if ((fd = open ("/dev/input/event0", O_RDONLY)) == -1){
printf ("Fehler mit Tastatur");
}else{
_TASKS_ACTIVE_=1;
}


struct input_event event;
while (_TASKS_ACTIVE_) {

if ((rd = read (fd, ev, size * 64)) < size)
printf ("Fehler mit Tastatur");

if (ev[1].type != EV_KEY) continue;
if (ev[1].value==0){ //Taste losgelassen
switch (ev[1].code) {
case 42: shiftl=0; break;
case 54: shiftr=0; break;
case 29: strgl=0; break;
case 97: strgr=0; break;
case 56: alt=0; break;
case 125: windows=0; break;
case 100: altgr=0; break;
case 127: kontext=0; break;
}
}else{
if (ev[1].value==1){ //==1 für nur einen Druck ohne Wiederholung. >=1 für Erkennung von gedrückt gehaltener Taste
modscode = 0;
switch (ev[1].code) {
case 42: shiftl=1; break;
case 54: shiftr=1; break;
case 29: strgl=1; break;
case 97: strgr=1; break;
case 56: alt=1; break;
case 125: windows=1; break;
case 100: altgr=1; modscode+=(2048+4096); break;
case 127: kontext=1; break;


// Ab hier 'normale Tasten'

//case 1: beenden=0; _TASKS_ACTIVE_=0; break; //ESC
default: keybscan=ev[1].code;// Scancode ablegen

if(shiftl || shiftr ) modscode+=1024;
if(strgl || strgr ) modscode+=2048;
if(alt) modscode+=4096;
if(altgr) modscode+=(2048+4096);


_kbscode_= keybscan + modscode;
printf("SHIFTL: %2d, SHIFTR: %2d, STRGL: %2d, STRGR: %2d; ",shiftl,shiftr,strgl,strgr);
printf("Typ: %2d; Wert: %2d; Code: %2d scancode=%6d \n",ev[1].type,ev[1].value,ev[1].code, _kbscode_ );
break;
}
}
}
}
beenden=0;
pthread_exit((void *) 0);
}

int main()
{
pthread_t thread1;
struct sched_param param;


if(pthread_create(&thread1, NULL, thread1Go, NULL) != 0)
{
fprintf(stderr, "Fehler bei Tastatur......\n");
exit(0);
}

param.sched_priority = 20;
pthread_setschedparam(thread1, SCHED_RR, &param);

beenden=1;

while (beenden > 0)
{ sleep(1);
zeit++;
if (zeit==30)
{
zeit=0;
printf("Wieder 30 Sekunden um.\n");
}
}
usleep(50);
printf("***********Ende************\n");
return 0;
}



was hältst du davon?



ps, Tipp für dich:
Ich habe mir sagen lassen, pthread verlinkt man besser per -pthread statt mit -lpthread.


You should really use -pthread rather than -lpthread as as well as including the library it also sets any other options (which usually on linux means setting -D_REENTRANT as well). So yes, if you use -lpthread then you need -D_REENTRANT otherwise things could go wrong.


Fragen an dich:
1) was verlinkt eigentlich -L/usr/local/lib ?
2) was ist "kontext" und "windows" ?

peterfido
06.04.2016, 15:37
Hallo,

L/usr/local/lib gibt noch einen Pfad hinzu, wo der Compiler nach den Libs suchen kann. Evtl. ist es bei g++ gar nicht nötig. Windows ist die Windows-Taste zwischen STRG+Alt und Kontext die Kontextmenütaste zwischen AltGr und STRG auf meiner Tastatur.

Sicher kann man einen eigenen Scancode erstellen. Das kommt auf den Anwendungsfall an. Bei freien Texteingaben ist es einzeln besser, da man die Buchstaben nur einmal abfragt und dann, ob Shift gedrückt ist. Möchte man einen Flipper programmieren, sind separate SHIFTs von Vorteil. Werden STRG UND SHIFT plus eine Taste gedrückt, passt es nicht mehr. Ist eine Makrotastatur vorhanden, empfiehlt es sich noch einen 'Tastaturpuffer' zu integrieren.

In zwei Projekten von mir sind als Bedienung je eine 10er Tastatur dran. Da brauche ich jede Taste inklusive der NumLock Taste.

Je nach Tastatur kann man mehrere Tasten gleichzeitig zuverlässig drücken. Gamer-Tastaturen lassen da normal mehr zu als 08/15 Tastaturen. Es lässt sich prinzipiell auch jede andere Taste als Modifiertaste nutzen. Und so z.B. für Codeeingaben auch die Drück- und Loslasssequenzen auswerten.

Aktuell brauche ich es nicht. Das wär was für die nächsten langen Winterabende (oder einen davon).

Meine Anfänge damit habe ich mit einer PS/2 Tastatur an einem Atmega gemacht. Da kann man dann auch die LEDs der Tastatur ansteuern. Da aber PS/2 bei den IR-Tastaturen und den 10er Tastaturen nicht so gängig ist, habe ich die Tastaturabfrage auf den Raspi ausgelagert.

Das mit den (l)pthread teste ich mal aus. Der von mir gefundene Beispielcode im Netz damals hatte es so gemacht.

Edit: AltGr durch STRG und Alt zu ersetzen klappt oft. Ist aber ein anderer Code. Windows z.B. reicht es nicht AltGr und Entf zu drücken ;)

HaWe
06.04.2016, 16:00
ah ja, jetzt hab ischs verstanden mit kontext und windows, hatte ich überlesen.

ps,
2 Zeilen werden offenbar nicht benötigt:

struct input_event event;
und

if (ev[1].type != EV_KEY) continue;

oder?

peterfido
06.04.2016, 16:31
Hallo,

auf den ersten Blick sieht es so aus, dass event sich da tatsächlich eingeschlichen hat. Das if schließt aus, dass bei einem 'falschen' Ereignistyp der Rest des Codes durchlaufen wird. Tritt kein Ereignis auf, ist der Typ 0 und dann würde ohne die Abfrage der Rest des Codes durchlaufen werden. So war zumindest der Gedanke. GeDebuggt habe ich nicht, ob es nach der Änderung des Codes noch notwendig ist.

Ansonsten melde ich mich erstmal ab, da ich eine Zeit lang offline sein werde.

HaWe
06.04.2016, 17:38
alles klar, auf jeden Fall nochmal vielen, vielen Dank, ich kann damit jetzt schon super arbeiten!
\\:D/

- - - Aktualisiert - - -

habs hingekriegt!
die Scanfunktion läuft jetzt in nem eigenen Funktions-Wrap!

Perfekt! Hilft mir jetzt super toll! :cool:



// Compile with: g++ -o /var/scripte/tastaturtest1 /var/scripte/tastaturtest1.c -L/usr/local/lib -lpthread
// /var/scripte/tastaturtest1
// 05.04.2016 - peterfido

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <sys/sysinfo.h>
#include <time.h>
#include <linux/input.h>
#include <pthread.h>
#include <errno.h>
#include <termios.h>
#include <signal.h>
#include <sys/types.h>
#include <fcntl.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/select.h>

int ziffer=0;
int beenden=2;
int zeit=0;
int shiftl=0;
int shiftr=0;
int strgl=0;
int strgr=0;
int capsl=0;
int alt=0;
int altgr=0;
int windows=0;
int kontext=0;
int keybscan=0;

int modscode;
volatile int _kbscode_ ;

// keyboard dev
int fkbd;
char * kbdin = "/dev/input/event0";
struct input_event ev[64];


int _TASKS_ACTIVE_= 1;



int getkbscancode() {
int rd, size = sizeof (struct input_event);

if ((rd = read (fkbd, ev, size * 64)) < size)
printf ("Fehler mit Tastatur");

if (ev[1].type != EV_KEY) return 0;

if (ev[1].value==0){ //Taste losgelassen
switch (ev[1].code) {
case 42: shiftl=0; break;
case 54: shiftr=0; break;
case 29: strgl=0; break;
case 97: strgr=0; break;
case 56: alt=0; break;
case 125: windows=0; break;
case 100: altgr=0; break;
case 127: kontext=0; break;
}
}
else
{
if (ev[1].value==1){
//==1 für nur einen Druck ohne Wiederholung. >=1 für Erkennung von gedrückt gehaltener Taste
modscode = 0;
switch (ev[1].code) {
case 42: shiftl=1; break;
case 54: shiftr=1; break;
case 29: strgl=1; break;
case 97: strgr=1; break;
case 56: alt=1; break;
case 125: windows=1; break;
case 100: altgr=1; modscode+=(2048+4096); break;
case 127: kontext=1; break;


// Ab hier 'normale Tasten'
default: keybscan=ev[1].code;// Scancode ablegen

if(shiftl || shiftr ) modscode+=1024;
if(strgl || strgr ) modscode+=2048;
if(alt) modscode+=4096;
if(altgr) modscode+=(2048+4096);
if(windows) modscode+=8192;
if(kontext) modscode+=16384;

if(keybscan>0) {
_kbscode_= keybscan + modscode;
return keybscan;
}
else
return 0 ;
//break;
}
}
}
return 0 ;
}

void* thread6Go(void *)
{


while (_TASKS_ACTIVE_) {

if(getkbscancode()) {
printf("\nSHIFTL: %2d, SHIFTR: %2d, STRGL: %2d, STRGR: %2d; ",shiftl,shiftr,strgl,strgr);
printf("Typ: %2d; Wert: %2d; Code: %2d scancode=%6d \n",ev[1].type,ev[1].value,ev[1].code, _kbscode_ );
}
}
beenden=0;
pthread_exit((void *) 0);
}

int main()
{
pthread_t thread6;
struct sched_param param;


if ((fkbd = open (kbdin, O_RDONLY)) == -1){
printf ("Fehler mit Tastatur");
}



if(pthread_create(&thread6, NULL, thread6Go, NULL) != 0)
{
fprintf(stderr, "Fehler bei Tastatur......\n");
exit(0);
}

param.sched_priority = 20;
pthread_setschedparam(thread6, SCHED_RR, &param);

beenden=1;

while (beenden > 0)
{ sleep(1);
zeit++;
if (zeit==30)
{
zeit=0;
printf("Wieder 30 Sekunden um.\n");
}
}
usleep(50);
printf("***********Ende************\n");
return 0;
}

HaWe
08.04.2016, 20:14
so, jetzt habe ich es zusammen mit anderen nützlichen keyboard-Funktionen in eine Lib gepackt:
die Funktion gibt den Scancode von "normalen" Tasten (0-9, a-z, F1-F12, cursor,...) zurück, ansonsten Null bei reinen Modifiern.
In jedem Falle kann man jetzt (bei Null) weiterhin auch sämtliche Modifier isoliert abfragen,
und bei >0 auch alle Events abfangen, die eine " Echte Taste" mit oder ohne Modifier betreffen.
Ebenfalls lassen sich jetzt für die gesamte Kombination eindeutige Scancode-Zahlen als auch diskrete Key-States abfragen, wie
_keyshift_, _keyalt_, _keyctrl_ und _F1_ ... _F12_ (noch ausbaufähig mit home, end, up, dn, ...)
Nun kann man keystrokes abfragen per

int c = getkbscancode();
if (_F1_) {...}
else
if(_F2_) {...}
else
if(_F1_ && _keyshift_) {...}
else
if(_F1_ && _keyshift_ && _keyalt_) {...}

usw...

Buchstabentasten lassen sich auch als scancodes lesen, aber natürlich ebenso, nach wie vor, auch als
c = getchar();



initialisiert wird in main() mit Aufruf von

setupKbdin() ;


share and enjoy! :)




#ifndef RPICONIO_H
#define RPICONIO_H

// courtesy of AndyD, raspberrypi.org forums, and peterfido, roboternetz.de forum
// version 002

#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <termio.h>
#include <unistd.h>

#include <linux/input.h>
#include <termios.h>
#include <signal.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/select.h>


// keyboard dev
int fkbd;
char * kbdin = "/dev/input/event0";
struct input_event ev[64];

int shiftl=0;
int shiftr=0;
int _keyshift_=0; // mod keypress state
int ctrll=0;
int ctrlr=0;
int _keyctrl_=0; // mod keypress state
int capsl=0;
int altl=0;
int altgr=0;
int _keyalt_=0; // mod keypress state
int windows=0;
int kontext=0;
int _keypress_; // keypress state
int modscode;
volatile int _kbscode_ ;

#define _F1_ 59
#define _F2_ 60
#define _F3_ 61
#define _F4_ 62
#define _F5_ 63
#define _F6_ 64
#define _F7_ 65
#define _F8_ 66
#define _F9_ 67
#define _F10_ 68
#define _F11_ 69
#define _F12_ 70


//************************************************** ***********
// conio.h - mimics
//************************************************** ***********

bool kbhit(void)
{
struct termios original;
tcgetattr(STDIN_FILENO, &original);

struct termios term;
memcpy(&term, &original, sizeof(term));

term.c_lflag &= ~ICANON;
tcsetattr(STDIN_FILENO, TCSANOW, &term);

int characters_buffered = 0;
ioctl(STDIN_FILENO, FIONREAD, &characters_buffered);

tcsetattr(STDIN_FILENO, TCSANOW, &original);

bool pressed = (characters_buffered != 0);

return pressed;
}

void echoOff(void)
{
struct termios term;
tcgetattr(STDIN_FILENO, &term);

term.c_lflag &= ~ECHO;
tcsetattr(STDIN_FILENO, TCSANOW, &term);
}

void echoOn(void)
{
struct termios term;
tcgetattr(STDIN_FILENO, &term);

term.c_lflag |= ECHO;
tcsetattr(STDIN_FILENO, TCSANOW, &term);
}




//************************************************** ***********
// keyboard scancodes
//************************************************** ***********

int getkbscancode() {
int rd, size = sizeof (struct input_event);
int keybscan=0; // scan code "normal key"


if ((rd = read (fkbd, ev, size * 64)) < size)
printf ("Fehler mit Tastatur");

if (ev[1].type != EV_KEY) return 0;

if (ev[1].value==0){ //Taste losgelassen
switch (ev[1].code) {
case 42: shiftl=0; break;
case 54: shiftr=0; break;
case 29: ctrll=0; break;
case 97: ctrlr=0; break;
case 56: altl=0; break;
case 125: windows=0; break;
case 100: altgr=0; break;
case 127: kontext=0; break;
}
}
else
{
if (ev[1].value==1){
//==1 fuer nur einen Druck ohne Wiederholung. >=1 fuer Erkennung von gedrueckt gehaltener Taste
modscode = 0;
switch (ev[1].code) {
case 42: shiftl=1; break;
case 54: shiftr=1; break;
case 29: ctrll=1; break;
case 97: ctrlr=1; break;
case 56: altl=1; break;
case 125: windows=1; break;
case 100: altgr=1; break;
case 127: kontext=1; break;

// Ab hier 'normale Tasten'
default: keybscan=ev[1].code;// Scancode ablegen

_keypress_ = keybscan; // standard keypress state
_keyshift_ = 0; // reset modifier key pressed
_keyalt_ = 0;
_keyctrl_ = 0;

if(shiftl || shiftr ) { modscode+=1024; _keyshift_=1; }
if(ctrll || ctrlr ) { modscode+=2048; _keyctrl_=1; }
if(altl) { modscode+=4096; _keyalt_=1; }
if(altgr) { modscode+=(2048+4096); _keyalt_=1; _keyctrl_=1; }
if(windows) modscode+=8192;
if(kontext) modscode+=16384;

if(keybscan>0) {
_kbscode_= keybscan + modscode;
return keybscan;
}
else return 0 ;
//break;
}
}
}
return 0 ;
}



int setupKbdin() {
if ((fkbd = open (kbdin, O_RDONLY)) == -1){
printf ("Fehler mit Tastatur");
return -1;
}
else return 0;
}


#endif

HaWe
09.04.2016, 19:12
OT:
habe immer so ein komisches "powerded by Google" Bild im Posting oben rechts - das engt das Codefenster extrem ein und mach auch editieren extremst schwierig, weil es den Text überlappt. Kann man das nicht abstellen?