🇩🇪

Source file: /~heha/mb-iwp/Kamera/esp32cam/cam-221216.zip/input.cpp

#include <Arduino.h>
#include "input.h"

Termcap termcap;
void Termcap::init() {
 width=80; height=24; flags=0;
 Serial.println("\e[6n");  // Cursorposition abfragen und Cursor nach links zurück
 char answer[16] __attribute__((unused));
 size_t alen=0;
 for (auto tic=millis();millis()-tic<100;) {
  auto c=Serial.read();
  if (c>=0) {
   if (alen<sizeof answer) answer[alen]=c;
   alen++;
  }
 }
 if (alen) fColor=true; // Wenn Antwort dann Terminal (putty) sonst dummer Arduino Serial Monitor
}

void Termcap::_setattr(unsigned n,...) const{
 if (!fColor) return;   // nichts ausspucken
 Serial.print('\e');
 Serial.print('[');
 va_list va;
 va_start(va,n);        // <n> durch Semikolon getrennte nichtnegative Zahlen ausgeben
 while (n) {
  Serial.print(va_arg(va,unsigned));    // Integer ausgeben (kein printf bemühen)
  if (!--n) break;
  Serial.print(';');
 }
 va_end(va);
 Serial.print('m');     // schließlich das "m" ausgeben
}

// String mit max. <bufsize-1> Zeichen auf serieller Konsole (Arduino, putty) eingeben
// Mit Kursorsteuerfunktion, ohne History, ohne Startmarkierung
// In einem Dialog müsste man mit ESC, TAB und evtl. vertikalen Kursortasten rauskommen
// Die Arduino-GUI 2.0.1 kommt NUR mit Startmarkierung zurecht und untestützt keine Farben.
// (vergebliche Liebesmüh, Zeileneditorfunktion nur bei Control-Zeichen, Löschen nicht möglich)
int input(char*buf,int bufsize,KeyCb keycb, void*cbdat) {
 auto backspace=[](int n=1) {if (n) do Serial.print('\b'); while (--n);};
 auto ding=[]() {return Serial.print('\a'),false;};  // Simple Lambdafunktion
 auto strcount=[](const char*s,const char*e) {  // Zählt "ganze" UTF8-Zeichen
  int i=0;
  while (s!=e) if (byte(*s++&0xC0)!=0x80) i++;  // Non-Trailbytes zählen
  return i;
 };
 auto hilite=[]() {termcap.setattr(1,37,44);};  // fett schaltet Vordergrundfarbe hell, zumindest bei ANSI.SYS, für mehr Kontrast
 auto unmark=[]() {termcap.setattr();};
 int l, i;  // i wird auf UTF8-Sequenzgrenzen gehalten
 auto goLeft=[&]() {
  if (!i) return ding();
  backspace();
  while (byte(buf[--i]&0xC0)==0x80 && i);  // UTF8-Trailbytes übergehen
  return true;
 };
 auto goRight=[&]() {
  if (i==l) return ding();
  Serial.print(buf[i++]);
  while (byte(buf[i]&0xC0)==0x80 && i<l) Serial.print(buf[i++]);  // UTF8-Trailbytes ausgeben
  return true;
 };
 auto delChar=[&]() {
	if (i==l) return ding();  // Cursor hinter letztem Zeichen geht nicht
  int j=i;
  while (++j!=l && byte(buf[j]&0xC0)==0x80);
  memmove(buf+i,buf+j,l-j+1);  // Zeichen bei Index löschen
  l-=j-i;      // String kürzen
  Serial.print(buf+i); // String-Rest ausgeben (kann leer sein)
  unmark();
  Serial.print(' '); // Leerzeichen zum Zeichen löschen dahinter, uninvertiert
  backspace(strcount(buf+i,buf+l)+1);  // Kursor repositionieren
  hilite();
  return true;
 };
 auto clearBuf=[&]() {   // String von vorn anfangen
  backspace(strcount(buf,buf+i)); i=l;
  unmark();
  if (i) do Serial.print(' '); while(--i);  // Dargestellte Zeichen löschen
  backspace(l);
  hilite();
  buf[l=0]=0;
 };
 auto onNewStr=[&]() {
  l=strlen(buf);
  if (l>bufsize-1) l=bufsize-1;  // Platz für '\0' behalten
  buf[i=l]=0;    // ggf. abhacken
  hilite();
  Serial.print(buf);    // Kursor dahinter
 };
// Hier geht's los
 onNewStr(); // Vorgabe ausspucken
// Mal schnell einen Zeileneditor gebastelt:
 int arg=-1;  // Numerisches Escape-Argument (nur eins)
 for(;;) {
  int c=Serial.read();
  if (c<0) continue;
  if (arg>=0) { // Escape-Modus?
   if ('0'<=c && c<='9') {arg=arg*10+c-'0'; continue;} // Ziffern zu Dezimalzahl zusammensetzen
   switch (c) {
    case '[':
    case 'O': continue;
// Kursortasten zum Editieren auswerten
// (Mit Shift markieren sowie Copy&Paste ist nicht möglich.)
    case 'C': c='F'-'@'; break;  // Pfeil rechts
    case 'D': c='B'-'@'; break;  // Pfeil links
    case 'K':
    case 'F': c='E'-'@'; break;  // Ende
    case 'H': c='A'-'@'; break;  // Pos1
    case '~': switch (arg) {
     case 1: c='A'-'@'; break;  // Pos1 (PuTTY)
     case 3: delChar(); c=-1; break; // Entf
     case 4: c='E'-'@'; break;  // Ende (PuTTY)
     default: c='\a';
    }break;
    default: c='\a';
   }
   arg=-1; // Ende Escape-Seqenz
  }
  if (arg<0 && c>=0) switch (c) {
   case 'A'-'@': backspace(strcount(buf,buf+i)); i=0; break;  // Pos1
   case 'B'-'@': goLeft(); break;
   case 'C'-'@': goto raus;     // Abbruch mit ^C
   case 'E'-'@': Serial.print(buf+i); i=l; break;  // Ende
   case 'F'-'@': goRight(); break;
   case 'P'-'@':
   case 'N'-'@': if (keycb) do switch (keycb(c,cbdat)) {
    case clear_call_again: clearBuf(); c=-1; continue;   // mit c==-1 erneut rufen; der zweite Callback sollte einen neuen String laden
    case string_replaced: onNewStr(); break;
    default: ding();
   }while(false); else ding();
   break;
   case '\a': ding(); break;  // Bimmel als -Echo
   case '\b':
   case 127: if (goLeft()) delChar(); break;
   case '\v':
   case '\t': ding(); break;  // TAB verwerfen
   case '\r': // Enter-Taste (mal so, mal so)
   case '\n': arg=l; goto raus; // Stringlänge reporten
   case 'U'-'@': clearBuf(); break;
   case '\e': arg=0; break;
// TODO: Akzentuierte Buchstaben bei Kursorpositionierung beachten
   default: // Buchstaben oder UTF-8-Bruchstücke
   int needspace=c&0x80 ? c&0x40 ? c&0x20 ? c&0x10 ? 4 : 3 : 2 : 0 : 1;
   if (l+needspace<bufsize-1) {
    if (needspace) {
     l+=needspace;
     memmove(buf+i+needspace,buf+i,l-i);  // String verlängern
     memset(buf+i,0xC2,needspace);
    }
    buf[i]=c;    // Byte einfügen
    Serial.print(buf+i++); // Ab da ausgeben
    backspace(strcount(buf+i,buf+l));  // Kursor repositionieren
   }else ding();
  }//switch
 }//for
raus:
 unmark();
 Serial.println();
 return arg;
}

int input(const char*prompt,char*buf,int bufsize,KeyCb keycb,void*cbdat) {
 Serial.print(prompt);
 Serial.print(": ");
 return input(buf,bufsize,keycb,cbdat);
}
Detected encoding: UTF-80