Input bar at console bottom in C

后端 未结 4 1144
Happy的楠姐
Happy的楠姐 2021-02-04 03:20

Window bottom

Some applications like vim, mutt, aptitude contain

  • a top window section for output and
  • a bottom section for the user to type in
相关标签:
4条回答
  • 2021-02-04 03:27

    I had a similar issue a few weeks ago while writing an IRC client that runs in the terminal. I wrote it using the Windows conio library, but I'm fairly sure this should be applicable to curses. The idea is that console output is handled by a single thread, and console input is handled in a separate thread. Basically, all you need is a loop that pushes the return of getch() onto a mutexed FIFO that runs for the duration of the program. In the display thread, you can pop the keypresses off the FIFO and handle them however you like. You can't use a standard function like fgets(), but it's a very solid solution to your problem. I can provide the full (messy) source on request.

    Edit: alright, well here's the relevant code from the FIFO pushing:

    bool keyqueuewriting = false;
    std::deque<int> keyqueue;
    void grabkey( void* in )
    {
        int t;
        while( true ){
            t = getch();
            #ifdef _WIN32
            if( t == 224 || t == 0 )
            {
                t += getch() << 8;
            }
            #else
                int off = 8;
                if( t == 27 ){
                    int e = getch();
                    t += e << off;
                    off += 8;
                    while( e ==91 || (e >= '0' && e <= '9') || e == ';' )
                    {
                        e = getch();
                        t += e << off;
                        off += 8;
                    }
                }
            #endif
            while( keyqueuewriting ){}
            keyqueuewriting = true;
            keyqueue.push_back( t );
            keyqueuewriting = false;
        }
    }
    

    And Handling:

    while( keyqueuewriting ){}
    keyqueuewriting = true;
    while( keyqueue.size() > 0 )
    {
        shouldsleep = false;
        int t = keyqueue.front();
        keyqueue.pop_front();
        switch( t )
        {
            case K_BACKSPACE:
                if( pos > 0 ){
                    for( int i = pos-1; input[i] != 0; i++ ){input[i] = input[i+1];}
                    movecursorback( 1 );
                    pos -= 1;
    
                    } break;
            case K_LEFT: if( pos > 0 ){ movecursorback( 1 ); pos -= 1; } break;
            case K_RIGHT: if( input[pos] != 0 ) {movecursorforward( 1 ); pos += 1;} break;
            case K_HOME: { gotoxy(0,SCREENHIG-1); pos = 0; } break;
            case K_END: { int a = strlen( input ); /*gotoxy( 79,39 );*/ pos = a;} break;
            case 3: exit(3); break;
    
            default: if( t >= 0x20 && t < 0x80 ){
                    int a = strlen( input );
                    if( a > 998 )
                        a = 998;
                    int deadcode = 1;
                    input[999] = 0;
                    for( int i = a+1; i > pos; i-- ){input[i] = input[i-1];}
                    input[ pos ] = t;
                    movecursorforward( 1 );
                    pos++;
                    } break;
        }
        change = bufpos[curroom] - bufprev;
        if( pos > 998 ) pos = 998;
        if( pos - mescroll < 1 ) {mescroll += (pos-mescroll-1); gotoxy( pos-mescroll, SCREENHIG-1 );}
        if( pos - mescroll > 78 ) {mescroll += (pos-mescroll-78); gotoxy( pos-mescroll, SCREENHIG-1 );}
        if( mescroll < 0 ) {mescroll = 0; gotoxy( 0, SCREENHIG-1 ); }
        savexy();
        gotoxy( 0, SCREENHIG-1 );
        char y = (input+mescroll)[79];
        (input+mescroll)[79] = 0;
        printf( "%s   ", input+mescroll );
        (input+mescroll)[79] = y;
    
        returntosaved();
        change2 = change;
        bufprev = bufpos[curroom];
    }
    keyqueuewriting = false;
    

    Yes, it uses std::deque. That should be the only C++ specific thing though. Just replace it with a C compatible FIFO.

    The entire client can be found here. Yes, it DOES compile in Linux, but it doesn't work. I never bothered to figure out how ncurses should be used before I started work on it.

    0 讨论(0)
  • 2021-02-04 03:41

    Using the standard libraries, there's no way to do that; using ncurses, as you already suggest, it is easily possible; I think this tutorial explains it quite nicely.

    0 讨论(0)
  • 2021-02-04 03:45

    This answer suggests a slightly different approach, but it avoids a lot of complication you'd introduce by using the current method. I hope it is possible to implement these simplifications.

    Use termio to turn off canonical mode.

    Sample code (here) will show you exactly how to set up such an input loop. This sample 'polls-and-sleeps' which could mean a delay unless you reduce the sleep. Also I think you can use termio to set a timeout (wait a second and return 'no input' or give me the input NOW if it comes earlier). But if you are really going to monitor another process, polling might be the more flexible option.. You can really poll 30x a second and live with the .00001% processor hit it is going to cause, but you will love the simplicity and bug prevention it gives.

    Avoid multiple threads and processes like the plague

    You don't need to use 2 processes/threads if the only problem you are trying to solve is the fact that getch() blocks. That would be required if it were impossible to prevent the input functions from blocking. 'Canonical' (rules based) means all sorts of handy 'rules' are in effect, like, 'don't give the input to the program until ENTER is hit'. For a full screen console app you want to turn off all the rules and do everything yourself.

    Put the main thread in charge of the window...

    ... Then you can just use the ansi escape csi codes to position the cursor back to where you want it. Caveat: you can't write to the lower-right box in the screen. Everything will scroll.

    There is an annoying thing in MS windows programming where only the thread that creates a window can safely update it. There is actually a reason for this. Whether you're talking console, or windowing system. sooner or later, if you have multiple threads/processing hitting one output device, you'll interrupt an escape sequence (or have to make extra code to manage this which is a bad thing), be fighting for the output 'port', etc. you need one thread to manage the output.

    if you really care about what some other thread or process is doing, just check it in your main console management loop. For example of you have another process that you just want to report its progress, start it from your program and capture its stdouput and look at that; another thread, just lock something shared you can check in your polling routine. Heck if it's just a byte and it's only used for statuses, don't even lock the damn thing. You can throw in a couple of GOTO's too, just to show your individuality :-)

    caveat I don't know that ncurses would play well with you if you are manually messing with termio? I would guess it wants to do that itself. Never tried mixing. If your app is simple you could go it alone without the help, especially if you can wrestle ncurses into doing what you want. I'm no expert on those apps you mention but I'd bet they are micromanaging everything.

    0 讨论(0)
  • 2021-02-04 03:49

    Using ANSI escape sequence it's possible to control the position of the cursor:

    void gotoxy(int x, int y) {
        printf("\033[%d;%dH",x,y);
    }
    

    So once you figure out the terminal height and width then you can use that function to position the cursor wherever you like and print stuff.

    0 讨论(0)
提交回复
热议问题