Forum: PC-Programmierung Terminal = Terminal?


von D a v i d K. (oekel) Benutzerseite


Lesenswert?

Hallo,

vermutlich wird es mir nicht gelingen in der initialen Frage das 
Kernproblem zu beschreiben, ich brobiere es dennoch mal ;)

Es geht mir darum, dass ich gerne für eine konsolenbasierte Anwwendung 
beliebige Tastatureingaben in einem dediziertem Threak abfangen und 
verwerten möchte. (Also nicht blockierend und auch ohne Bestätigung mit 
Return)

Ich habe schon einige Code-Schnipsel gefunden, wie dieses 
"nicht-blockiernd" funktionieren soll (Win & Unix) und auch einige 
Teile, wie man kbhit() und getch() unter Linux nachbildet.

Jedoch blockieren diese ALLE in meiner Konsole, die in der IDE 
eingebunden ist.
(Veränderbar und nutzt per Default auch nur den System-Std)

Daher möchte ich unabhängig von der Programmierung hier erst mal klären, 
welche Voraussetzungen man überhaupt braucht, damit ALLE Eingaben 
gefangen werden können und ob dies neben dem "Fokus" auf dem 
ausführenden Terminal noch andere Kriterien erfüllen muss.

Grüße David

von Adam P. (adamap)


Lesenswert?

Möchtest du nun eine nicht blockierende Eingabe im Terminal,
oder soll dein Programm Eingaben aufzeichnen auch wenn dein Progamm 
nicht den Fokus besitzt?

von Walter T. (nicolas)


Lesenswert?

Oder willst Du einfach ein interaktives Programm in der Konsole?

von D a v i d K. (oekel) Benutzerseite


Lesenswert?

Adam P. schrieb:
> Möchtest du nun eine nicht blockierende Eingabe im Terminal,
Ja, den Fokus bekomme ich schon noch geregelt.

Walter T. schrieb:
> Oder willst Du einfach ein interaktives Programm in der Konsole?
Was versteht man hier unter "interaktiv"?

Ich glaube ein gutes Beispiel wäre "Snake" oder "Packman".
(auch wenn es hier nicht konkret um ein Spiel geht)

Ermöglicht werden soll z.B.
$ "Start"
$ Buffer ausgabe:[ 0, 0, 1, 4, 6]... (Über den Hauptthread)
$ Buffer ausgabe:[ 0, 0, 1, 4, 6]... (Über den Hauptthread)
$ Buffer ausgabe:[ 0, 0, 1, 4, 6]... (Über den Hauptthread)
$ 'a'
$ "Sie befinden sich nun in Menue a"
$ Buffer ausgabe:[ 0, 0, 1, 4, 6]... (Über den Hauptthread)
$ Buffer ausgabe:[ 0, 0, 1, 4, 6]... (Über den Hauptthread)
$ 'Pfeiltaste-UP'
$ Buffer ausgabe:[ 0, 0, 1, 4, 6]... (Über den Hauptthread)
$ "Der globale Wert int x wurde incrementiert und ist nun x = 10"
$ Buffer ausgabe:[ 0, 0, 1, 4, 6]... (Über den Hauptthread)
$ 'Pfeiltaste-DOWN'
$ Buffer ausgabe:[ 0, 0, 1, 4, 6]... (Über den Hauptthread)
$ Buffer ausgabe:[ 0, 0, 1, 4, 6]... (Über den Hauptthread)
$ "Der globale Wert int x wurde decrementiert und ist nun x = 9"
$ Buffer ausgabe:[ 0, 0, 1, 4, 6]... (Über den Hauptthread)
$ Buffer ausgabe:[ 0, 2, 1, 4, 6]... (Über den Hauptthread)
$ Buffer ausgabe:[ 0, 4, 1, 4, 6]... (Über den Hauptthread)
$ Buffer ausgabe:[ 0, 0, 1, 4, 6]... (Über den Hauptthread)
$...
$...
$...
Grüße David

von T.roll (Gast)


Lesenswert?

D a v i d K. schrieb:
> Es geht mir darum, dass ich gerne für eine konsolenbasierte Anwwendung
> beliebige Tastatureingaben in einem dediziertem Threak abfangen und
> verwerten möchte. (Also nicht blockierend und auch ohne Bestätigung mit
> Return)

Du möchtest also einen Keylogger programmieren. Sags doch gleich.

von Adam P. (adamap)


Lesenswert?

D a v i d K. schrieb:
> Adam P. schrieb:
>> Möchtest du nun eine nicht blockierende Eingabe im Terminal,
> Ja, den Fokus bekomme ich schon noch geregelt

Bezweifel grad das du die Problematik verstehst...
wenn dein Programm den Fokus nicht besitzt, dann musst du "keyboard 
hooks" verwenden, sprich du musst dich an windows registrieren und ihm 
mitteilen, dass alle aktionen ebenfalls an dich geleitet werden sollen.

Was dein parallele Verarbeitung betrifft:
Das sollte doch mit Threads ganz gut funktionieren.
- Erstell ein Thread der sich um das Einlesen kümmert und die Daten 
global zur Verfügung stellt.
- In der main() machst eine while() die immer etwas ausgibt oder 
verarbeitet und dort reagierst du bzw. prüfst ob etwas eingegeben wurde.

von Adam P. (adamap)


Lesenswert?

Natürlich muss man bei Thread noch den gleichzeitigen Zugriff auf 
Variablen beachten etc.

Aber vllt. ist ja das Beispiel das was du vor hast:

Findest zu Windows-Threads hier z.B. ein Tutorial:
https://www.spieleprogrammierer.de/27-tutorials/6661-einstieg-in-multithreading-unter-windows/

1
#include <stdio.h>
2
#include <conio.h>
3
#include <windows.h>
4
#include <time.h>
5
6
bool input;
7
char ch;
8
9
/*******************************************************************************
10
* Nicht schön, dient nur der langsameren Ausgabe in der main()
11
*/
12
void delay(int number_of_seconds)
13
{
14
  // Converting time into milli_seconds 
15
  int milli_seconds = 1000 * number_of_seconds;
16
17
  // Stroing start time 
18
  clock_t start_time = clock();
19
20
  // looping till required time is not acheived 
21
  while (clock() < start_time + milli_seconds);
22
}
23
24
/*******************************************************************************
25
* Thread für Eingabe
26
*/
27
DWORD WINAPI ThreadProc(LPVOID lpParam)
28
{
29
  while (1)
30
  {
31
    ch = _getch();
32
33
    input = true;
34
  }
35
36
  return 0;
37
}
38
39
/*******************************************************************************
40
* Haupt-Thread
41
*/
42
int main()
43
{
44
  DWORD threadId, mainThreadId;
45
  HANDLE threadHandle;
46
  bool ende = false;
47
48
  mainThreadId = GetCurrentThreadId();
49
  printf("Ich bin das Hauptprogrammm: ID %d\n", mainThreadId);
50
51
  threadHandle = CreateThread(NULL, 0, ThreadProc, NULL, 0, &threadId);
52
  printf("Thread mit der ID=%d erstellt\n", threadId);
53
54
  printf("\n\n");
55
56
  while (!ende)
57
  {
58
    printf("Hauptprogramm\n");
59
60
    if (input)
61
    {
62
      input = false;
63
      printf("\nEingabe: %c\n", ch);
64
65
      if (ch == 'E')
66
      {
67
        ende = true;
68
      }
69
    }
70
71
    /* Damit die Ausgabe nicht 1000x die Sekunde passiert */
72
    delay(1);
73
  }
74
75
  CloseHandle(threadHandle);
76
77
  return 0;
78
}

von D a v i d K. (oekel) Benutzerseite


Lesenswert?

Adam P. schrieb:
> Natürlich muss man bei Thread noch den gleichzeitigen Zugriff auf
> Variablen beachten etc.
>
> Aber vllt. ist ja das Beispiel das was du vor hast:
> {
>   while (1)
>   {
>     ch = _getch();
>
>     input = true;
>   }
Egal wie ich diesen teil hier umsetze (also hab bestimmt schon 8 
verschiedene Implementierungen "reinkopiert" Funktioniert immer nur die 
Erkennung NACH dem betätigen der Enter-Taste.
(Ich weiß kopiere, statt verstehen ist hässlich, aber es geht ja nun mal 
um grundsätzliche Dinge in meiner IDE)

Adam P. schrieb:
> D a v i d K. schrieb:
>> Adam P. schrieb:
>>> Möchtest du nun eine nicht blockierende Eingabe im Terminal,
>> Ja, den Fokus bekomme ich schon noch geregelt
>
> Bezweifel grad das du die Problematik verstehst...
Bezweifel ich ja selbst ;)

von 50c (Gast)


Lesenswert?

nur mal so zum Einlesen...:
https://www.geeksforgeeks.org/difference-getchar-getch-getc-getche/

Die Frage ist ja eigentlich eine andere: passt die gewählte 
Programmstruktur auf dass, was implementiert werden soll.

Wenn man sich nicht unbedingt mit Threads rumärgern möchte, dann steht 
halt die Tastatureingabe in einem Endlos-Loop in main und alles, was man 
während des Wartens auf eine Tastatureingabe nebenbei machen will, packt 
man z.B. in einen Timerinterrupt.

von D a v i d K. (oekel) Benutzerseite


Angehängte Dateien:

Lesenswert?

50c schrieb:
> nur mal so zum Einlesen...:
> https://www.geeksforgeeks.org/difference-getchar-getch-getc-getche/

Danke, hier noch zwei Bilder, die demonstrieren sollen, dass ich nicht 
zu blöde bin Code auszuführen ;P

von 50c (Gast)


Lesenswert?

bash != Terminal

von Walter T. (nicolas)


Lesenswert?

Habe ich das richtig verstanden: Du willst ein einfaches, interaktives 
Konsolenprogramm schreiben, das sich vom normalen input() dadurch 
unterscheidet, dass zeichenweise reagiert wird, und nicht erst eine 
Eingabe komplett abgeschlossen sein muss?

Wenn ich das richtig verstanden habe, ist evtl. SDL2 Dein Freund. Da 
kann man Keyboard-Events relativ einfach abfragen.

: Bearbeitet durch User
von Sven B. (scummos)


Lesenswert?

Es gibt verschiedene Terminalemulatoren. Es kann gut sein, dass der in 
deiner IDE das Feature, was du brauchst, nicht unterstützt, andere 
hingegen schon.

So einen full-featured Terminalemulator schreiben ist ein relativ 
komplexes Projekt, und es ist gut möglich dass $IDE da nur die 
offensichtlichsten 3 Dinge eingebaut hat.

: Bearbeitet durch User
von foobar (Gast)


Lesenswert?

Ich hab den Verdacht, dass das "Terminal" in der IDE sein eigenes 
Süppchen kocht (ein eigener Zeileneditor (in grün)) und erst bei Return 
die Zeile an die Anwendung weitergibt. Das Teil kann wahrscheinlich gar 
kein Single-Char-Input. Starte in dem Teil mal nen Texteditor ...


PS: deine getch-Routine sollte auch VMIN und VTIME setzen.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Ja, der interne Terminalemulator von CLion scheint da ein paar
Einschränkungen zu haben. So kannst mit CLion einen externen
Terminalemulator nutzen (hier gnome-terminal, sollte aber genauso
mit einem anderen funktionieren):

  https://stackoverflow.com/questions/36675012/how-to-execute-a-clion-program-in-gnome-terminal

Ich verstehe sowieso nicht, warum Leute in der heutigen Zeit immer noch
IDEs benutzen ;-)

von Adam P. (adamap)


Lesenswert?

D a v i d K. schrieb:
> Egal wie ich diesen teil hier umsetze (also hab bestimmt schon 8
> verschiedene Implementierungen "reinkopiert" Funktioniert immer nur die
> Erkennung NACH dem betätigen der Enter-Taste.

So wie ich es gepostet habe ist es das ganze Programm....nichts heraus 
kopieren, versuch es so zu kompilieren und zu verstehen.

getch() erwartet kein ENTER, deshalb muss der Fehler in deinem Code 
liegen.


Was nutzt du für eine IDE?
Ich hab es mit dem Visual Studio 2015 oder 2017 unter Win 10 getestet 
und brauchte kein ENTER.

: Bearbeitet durch User
von R. M. (rmax)


Lesenswert?

D a v i d K. schrieb:
> Egal wie ich diesen teil hier umsetze (also hab bestimmt schon 8
> verschiedene Implementierungen "reinkopiert" Funktioniert immer nur die
> Erkennung NACH dem betätigen der Enter-Taste.

Das liegt daran, dass Terminals (genau genommen TTYs) unter Unix die 
eingegebene Zeile normalerweise erst nach Drücken der Enter-Taste 
absenden, d.h. davor hat Dein Prozeß gar keine Chance die Eingaben zu 
sehen, egal welche Input-Funktion er verwendet. Um das zu ändern, muss 
das TTY in den raw-Modus versetzt werden.

Auf die Schnelle habe ich dieses Tutorial gefunden, das die Thematik 
recht ausführlich behandelt:

https://viewsourcecode.org/snaptoken/kilo/02.enteringRawMode.html

Falls Du es einfacher haben möchtest und Dich der Hintergrund nicht 
interessiert, kannst Du z.B. die ncurses-Bibliothek verwenden. Da gibt 
es die Funktionen raw() und noraw(), um zwischen den beiden Modi 
umzuschalten und auch die getch()-Funktion nach der Du im Eingangspost 
gefragt hattest.

: Bearbeitet durch User
von Adam P. (adamap)


Lesenswert?

Ja gut wenn er grad unter Linux arbeitet, wird mein Code nicht laufen...

Mein Bsp. setzt auf den Win-Threads auf, sonst muss er pthread nutzen!

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

R. M. schrieb:
> Das liegt daran, dass Terminals (genau genommen TTYs) unter Unix die
> eingegebene Zeile normalerweise erst nach Drücken der Enter-Taste
> absenden

Da die Dinger ursprünglich an seriellen Schnittstellen angeschlossen 
waren: Wie sollen die das machen?

Wie soll damit z.B. ein Bildschirmeditor funktionieren? vi 
beispielsweise reagiert auf einzelne Tastendrücke ...

Und ein vt100 sendet jeden Tastendruck einzeln, das hat weder einen 
Zeilenpuffer noch sendet es erst nach der Entertaste.

Das Problem hier dürfte sich buffered IO nennen, und liegt nicht im 
Terminal, sondern ist ein Teil des Betriebssystems. Der lässt sich auch 
abschalten (sonst würde wie gesagt kein Bildschirmeditor funktionieren).

Das sollte mit setvbuf mit dem Modus _IONBF möglich sein.

von D a v i d K. (oekel) Benutzerseite


Lesenswert?

Adam P. schrieb:
> So wie ich es gepostet habe ist es das ganze Programm....nichts heraus
> kopieren, versuch es so zu kompilieren und zu verstehen.
>
> getch() erwartet kein ENTER, deshalb muss der Fehler in deinem Code
> liegen.
>
>
> Was nutzt du für eine IDE?
> Ich hab es mit dem Visual Studio 2015 oder 2017 unter Win 10 getestet
> und brauchte kein ENTER.

Ich stelle das hier immer wieder fest (nur du musst jetzt leider mal 
dran glauben), dass scheinbar lesen nicht so die Stärke einiger 
"Hilfesteller" ist?

Man erwartet vom Fragesteller, dass er im Kontext bleibt aber die 
Antworten driften immer weiter ab oder wiederholen sogar Annahmen, die 
man bereits verworfen hat.

Wie ich Threads erzeuge weiß ich, ist aber doch ein ganz anderes Thema!
(notfalls geht immer ein while(1){}
Welche IDE und welches Betriebsystem ich verwende sollte ebenfalls seit 
dem posten des Screenshots klar sein!


An viele andere ein herzlichen Dank! Ich denke ich habe das Problem in 
etwa verstanden und versuche meine IDE nun umzukonfigurieren.

Falls dies nicht klappt, könnt ihr mir noch in einem Satz die 
Anforderung auf den Punkt bringen, die IDEA (Clion & IntelliJ) in einem 
Ticket hören wollen würde, wenn ich das als BUG/FEATURE melde?

(Bei IntelliJ z.B. merke ich dass die Jungs ihre IDE auch selber 
verwenden, denn die Usability ist extrem hoch und auch die Updates incl. 
EAP sehr häufig. Daher besteht eine Chance dass die es wirklich mit 
einbauen)

Grüße David

: Bearbeitet durch User
von Sven B. (scummos)


Lesenswert?

Die andere Frage ist halt, ist das wirklich so zentral dass das im 
Terminalemulator der IDE läuft? Warum nicht einen externen nehmen?

Im Endeffekt muss es ja eh dort laufen, und wenn die sich 
unterschiedlich verhalten ...

: Bearbeitet durch User
von D a v i d K. (oekel) Benutzerseite


Lesenswert?

Sven B. schrieb:
> Die andere Frage ist halt, ist das wirklich so zentral dass das im
> Terminalemulator der IDE läuft? Warum nicht einen externen nehmen?
>
> Im Endeffekt muss es ja eh dort laufen, und wenn die sich
> unterschiedlich verhalten ...

ein deutliches JEIN, ich "simuliere" hier Dinge, die eigentlich auf 
einem AVR ablaufen. Also möchte ich Eingaben wie Knopfdrücken über die 
Tastatur abhandeln.
Beitrag "CMake zwei Compilate bauen lassen"

Grüße David

von Rolf M. (rmagnus)


Lesenswert?

D a v i d K. schrieb:
> Hallo,
>
> vermutlich wird es mir nicht gelingen in der initialen Frage das
> Kernproblem zu beschreiben, ich brobiere es dennoch mal ;)
>
> Es geht mir darum, dass ich gerne für eine konsolenbasierte Anwwendung
> beliebige Tastatureingaben in einem dediziertem Threak abfangen und
> verwerten möchte. (Also nicht blockierend und auch ohne Bestätigung mit
> Return)

Nicht blockierend ist schlecht. Das bedeutet, dass dein Thread einen 
Prozessorkern zu 100% damit auslastet, auf Eingaben zu warten. Ich 
verstehe auch den Sinn nicht. Wenn du schon einen eigenen Thread dafür 
hast, warum darf der nicht blockieren?

> Ich habe schon einige Code-Schnipsel gefunden, wie dieses
> "nicht-blockiernd" funktionieren soll (Win & Unix) und auch einige
> Teile, wie man kbhit() und getch() unter Linux nachbildet.

Unter Linux kann man man das getch() von ncurses verwenden.
https://linux.die.net/man/3/getch

von Sven B. (scummos)


Lesenswert?

Rolf M. schrieb:
> Nicht blockierend ist schlecht. Das bedeutet, dass dein Thread einen
> Prozessorkern zu 100% damit auslastet, auf Eingaben zu warten.

Das ist jetzt nicht per se richtig. Es könnte auch ein Callback 
aufgerufen werden wenn ein Ereignis stattfindet.

von Rolf M. (rmagnus)


Lesenswert?

Und was macht der Thread dann in der Zeit, in der kein Callback 
aufgerufen wird? Entweder er blockiert, oder er lastet den Kern komplett 
aus. Anders geht's nicht.

von Walter T. (nicolas)


Lesenswert?

D a v i d K. schrieb:
> ein deutliches JEIN, ich "simuliere" hier Dinge, die eigentlich auf
> einem AVR ablaufen.

Dann würde ich wirklich mal einen Blick auf SDL werfen. Spätestens, wenn 
Du auch ein Display emulieren willst.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.