2016-10-29 2 views
1

Was ich versuche zu tun, ein Chat-Fenster, wo Text kontinuierlich aktualisiert wird, und dann ein Eingabefenster, in dem ein Benutzer eine Nachricht eingeben kann hinzufügen zum Chat.Erhalten Sie volle Strings aus `getstr` in ncurses in einer nicht blockierenden Weise

Ich frage mich, ob es möglich ist, getstr() mit Krankenschwestern in einer solchen Weise zu verwenden, dass getstr() nur eine Zeichenfolge an mich zurückgibt, sobald ich die Eingabetaste gedrückt habe.

Dies würde erlauben Sie mir, um kontinuierlich die TCP-Verbindung für neue Nachrichten zu überwachen und dann eine Nachricht an den Chat hinzufügen, sobald der Benutzer seine/ihre volle Meldung eingegeben.

Ich habe Beiträge gesehen, die den Benutzer von timeout() vorschlagen, jedoch verursacht dies getstr(), was auch immer der Benutzer eingegeben hat, wenn der Benutzer für eine gewisse Zeit inaktiv war. Dies ist nicht wirklich das, was ich suche, wie in diesem Fall getstr() noch blockiert, während der Benutzer tippt, und sollte ein Benutzer aufhören, darüber nachzudenken, was sie auf halbem Weg durch die Nachricht schreiben, wird dieser Text von getstr() vor dem Benutzer zurückgegeben hat bestätigt, dass dies die Nachricht ist, die er/sie senden möchte, indem er die Eingabetaste drückt.

Ich bin besonders neugierig auf getstr() zu arbeiten, als dann muss ich nicht manuell manuell Löschen/Backspace/Cursor Bewegungen Operationen manuell. Angesichts des Umfangs meines Projekts ist es ein bisschen zu groß für eine Investition.

+0

Wenn Sie andere Teile des Bildschirms machen möchten, während der Benutzer die Eingabe ist die Eingabe, wird es erforderlich machen den Cursor zu bewegen, so würde dies immer möglich Rennen führen (wenn der Benutzer während der Renderer noch Zeichen senden). Aus diesem Grund befürchte ich, dass Sie Ihre eigene Handhabung von links/rechts/delete/backspace implementieren. Aber wenn Sie nichts rendern müssen, können Sie einfach einen zweiten Thread verwenden. – Dave

+0

Also könnte ich einen zweiten Thread verwenden, um die Eingabe zu greifen und die Eingabe bei Bedarf zum Hauptprogramm weiterzuleiten? Dies würde es dem Hauptprogramm ermöglichen, eingehende Nachrichten weiterzugeben. –

+0

Nein, ncurses kann nicht aus mehreren Threads verwendet werden (daher ist Threading nur eine Option, wenn Sie nicht rendern müssen, während Sie auf Eingaben warten). Denken Sie daran, dass ncurses nur stdin und stdout verwendet, um mit dem Terminal zu sprechen. Sie sind also auf einen einzigen Cursor beschränkt, der sowohl zum Rendern als auch zum Lesen verwendet werden muss. – Dave

Antwort

1

Wie es passiert, ich arbeite auch auf etwas mit einer ähnlichen Anforderung (keine Chat-Anwendung, obwohl).

Um es zu wiederholen, was ich gesagt: wenn Sie etwas nicht machen müssen, brauchen Sie nur einen zweiten Thread nutzen könnten. Buf, wenn Sie tun müssen, während Sie auf Eingabe warten, müssen Sie die Eingabe selbst durchführen.

Warum? weil Ncurses nur über stdin/stdout mit dem Terminal spricht. Das bedeutet, dass Sie nur einen Cursor für die Eingabe und Ausgabe erhalten. Wenn Sie also den Cursor bewegen, um eine Ausgabe auszugeben, wird die laufende Eingabe durcheinander gebracht.

Aber es ist nicht so schwer, die Eingabe selbst zu interpretieren & machen. Hier ist eine reduzierte Version meiner ersten Pass Lösung:

// Compile with -lncurses 

#include <ncurses.h> 
#include <string.h> 
#include <ctype.h> 
#include <stdlib.h> 

struct input_line { 
    char *ln; 
    int length; 
    int capacity; 
    int cursor; 
    int last_rendered; 
}; 

void make_buffer(struct input_line *buf) { 
    buf->ln = NULL; 
    buf->length = 0; 
    buf->capacity = 0; 
    buf->cursor = 0; 
    buf->last_rendered = 0; 
} 

void destroy_buffer(struct input_line *buf) { 
    free(buf->ln); 
    make_buffer(buf); 
} 

void render_line(struct input_line *buf) { 
    int i = 0; 
    for(; i < buf->length; i ++) { 
     chtype c = buf->ln[i]; 
     if(i == buf->cursor) { 
      c |= A_REVERSE; 
     } 
     addch(c); 
    } 
    if(buf->cursor == buf->length) { 
     addch(' ' | A_REVERSE); 
     i ++; 
    } 
    int rendered = i; 
    // Erase previously rendered characters 
    for(; i < buf->last_rendered; i ++) { 
     addch(' '); 
    } 
    buf->last_rendered = rendered; 
} 

int retrieve_content(struct input_line *buf, char *target, int max_len) { 
    int len = buf->length < (max_len - 1) ? buf->length : (max_len - 1); 
    memcpy(target, buf->ln, len); 
    target[len] = '\0'; 
    buf->cursor = 0; 
    buf->length = 0; 
    return len + 1; 
} 

void add_char(struct input_line *buf, char ch) { 
    // Ensure enough space for new character 
    if(buf->length == buf->capacity) { 
     int ncap = buf->capacity + 128; 
     char *nln = (char*) realloc(buf->ln, ncap); 
     if(!nln) { 
      // Out of memory! 
      return; 
     } 
     buf->ln = nln; 
     buf->capacity = ncap; 
    } 

    // Add new character 
    memmove(
     &buf->ln[buf->cursor+1], 
     &buf->ln[buf->cursor], 
     buf->length - buf->cursor 
    ); 
    buf->ln[buf->cursor] = ch; 
    ++ buf->cursor; 
    ++ buf->length; 
} 

int handle_input(struct input_line *buf, char *target, int max_len, int key) { 
    if(!(key & KEY_CODE_YES) && isprint(key)) { 
     add_char(buf, key); 
     return 0; 
    } 

    switch(key) { 
    case ERR: /* no key pressed */ break; 
    case KEY_LEFT: if(buf->cursor > 0)   { buf->cursor --; } break; 
    case KEY_RIGHT: if(buf->cursor < buf->length) { buf->cursor ++; } break; 
    case KEY_HOME: buf->cursor = 0;   break; 
    case KEY_END: buf->cursor = buf->length; break; 
    case '\t': 
     add_char(buf, '\t'); 
     break; 
    case KEY_BACKSPACE: 
    case 127: 
    case 8: 
     if(buf->cursor <= 0) { 
      break; 
     } 
     buf->cursor --; 
     // Fall-through 
    case KEY_DC: 
     if(buf->cursor < buf->length) { 
      memmove(
       &buf->ln[buf->cursor], 
       &buf->ln[buf->cursor+1], 
       buf->length - buf->cursor - 1 
      ); 
      buf->length --; 
     } 
     break; 
    case KEY_ENTER: 
    case '\r': 
    case '\n': 
     return retrieve_content(buf, target, max_len); 
    } 
    return 0; 
} 

int get_line_non_blocking(struct input_line *buf, char *target, int max_len) { 
    while(1) { 
     int key = getch(); 
     if(key == ERR) { 
      // No more input 
      return 0; 
     } 
     int n = handle_input(buf, target, max_len, key); 
     if(n) { 
      return n; 
     } 
    } 
} 

int main(void) { 
    initscr(); 

    cbreak();    // Immediate key input 
    nonl();    // Get return key 
    timeout(0);   // Non-blocking input 
    keypad(stdscr, 1); // Fix keypad 
    noecho();    // No automatic printing 
    curs_set(0);   // Hide real cursor 
    intrflush(stdscr, 0); // Avoid potential graphical issues 
    leaveok(stdscr, 1); // Don't care where cursor is left 

    struct input_line lnbuffer; 
    make_buffer(&lnbuffer); 

    int lines_read = 0; 
    while(1) { 
     char ln[1024]; 
     int len = get_line_non_blocking(&lnbuffer, ln, sizeof(ln)); 
     if(len > 0) { 
      if(strcmp(ln, "exit") == 0) { 
       break; 
      } 
      mvaddstr(7 + lines_read, 5, ln); 
      lines_read ++; 
     } 
     move(5, 5); 
     render_line(&lnbuffer); 

     // Show that we are active 
     mvaddch(2, 2, '0' + (rand() % 10)); 
     // (probably a good idea to sleep here) 
    } 

    destroy_buffer(&lnbuffer); 
    delwin(stdscr); 
    endwin(); 
    refresh(); 

    return 0; 
} 

Es gibt viele Steuerzeichen, die nicht dort umgesetzt worden (vor allem INSERT), aber es sollte ganz einfach sein, alles zu addieren denken Sie, ist wichtig, zu Ihrer speziellen Anwendung. Beachten Sie auch, dass Sie, wenn Sie Unicode (empfohlen) möchten, ncursesw und seine alternativen Funktionen verwenden müssen.

+0

Das hat wunderbar funktioniert.Ich musste ein bisschen modifizieren, um es mit Unicode arbeiten zu lassen, aber es war überhaupt kein Problem. Vielen Dank! –

Verwandte Themen