Multithreaded console I/O

前端 未结 3 908
旧巷少年郎
旧巷少年郎 2020-12-31 16:30

I\'m using a console in my multithreaded application. Right now, it only accepts output (printf and the like) and so far I have no issues. However, I want to be able to su

3条回答
  •  暖寄归人
    2020-12-31 16:57

    Wellp, I solved it using pdcurses. In case someone else wants to do something similar, here's how I did it. First, I initialize the console thusly:

    Console::Console(bool makeConsole)
    {
        if (makeConsole == false)
            return;
    
        if (self)
            throw ("You only need one console - do not make another!\n");
        self = this;
    
    #ifdef WIN32
        AllocConsole();
    #endif
        initscr();
    
        inputLine = newwin(1, COLS, LINES - 1, 0);
        outputLines = newwin(LINES - 1, COLS, 0, 0);
    
        if (has_colors())
        {
            start_color();
            for (int i = 1; i <= COLOR_WHITE; ++i)
            {
                init_pair(i, i, COLOR_BLACK);
            }
        }
        else
            wprintw(outputLines, "Terminal cannot print colors.\n");
    
        scrollok(outputLines, TRUE);
        scrollok(inputLine, TRUE);
    
        leaveok(inputLine, TRUE);
        nodelay(inputLine, TRUE);
        cbreak();
        noecho();
        keypad(inputLine, TRUE);
    
        initCommands();
    
        hello("Starting %s.\n", APP_NAME);
        hellomore("Version %i.%i.%i.\n\n", APP_MAJORVER, APP_MINORVER, APP_REVISION);
    }
    

    Next, This is the function responsible for handling output. It's actually very simple, I don't need to do anything special to keep it thread-safe. I might simply not have encountered any issues with it, but an easy fix would be to slap a mutex on it.

    void Console::sendFormattedMsg(short prefixColor, const char* prefix, short color, const char* format, ...)
    {
        if (!self)
            return;
    
        va_list args;
        va_start(args, format);
    
        if (has_colors())
        {
            if (prefix)
            {
                wattron(outputLines, A_BOLD | COLOR_PAIR(prefixColor));
                wprintw(outputLines, prefix);
            }
    
            if (color == COLOR_WHITE)
                wattroff(outputLines, A_BOLD);
            wattron(outputLines, COLOR_PAIR(color));
            vwprintw(outputLines, format, args);
    
            wattroff(outputLines, A_BOLD | COLOR_PAIR(color));
        }
        else
        {
            wprintw(outputLines, prefix);
            vwprintw(outputLines, format, args);
        }
    
        wrefresh(outputLines);
        va_end(args);
    }
    

    And finally, input. This one required quite a bit of fine-tuning.

    void Console::inputLoop(void)
    {
        static string input;
    
        wattron(inputLine, A_BOLD | COLOR_PAIR(COLOR_WHITE));
        wprintw(inputLine, "\n> ");
        wattroff(inputLine, A_BOLD | COLOR_PAIR(COLOR_WHITE));
    
        wprintw(inputLine, input.c_str());
        wrefresh(inputLine);
    
        char c = wgetch(inputLine);
        if (c == ERR)
            return;
    
        switch (c)
        {
        case '\n':
            if (input.size() > 0)
            {
                sendFormattedMsg(COLOR_WHITE, "> ", COLOR_WHITE, input.c_str());
                cprint("\n");
    
                executeCommand(&input[0]);
                input.clear();
            }
            break;
    
        case 8:
        case 127:
            if (input.size() > 0) input.pop_back();
            break;
    
        default:
            input += c;
            break;
        }
    }
    

    This is run every frame from the same thread that handles window messages. I disabled wgetch()'s blocking behavior using nodelay(), eliminating the need to have console input running in it's own thread. I also disable echoing and echo the input manually. Enabling scrolling on the input window allows me to clear it's contents using a simple "\n", replacing it with updated contents if the user has typed anything. It supports everything one would expect from a simple, multi-threaded terminal capable to typing input as well as receiving output from multiple threads.

提交回复
热议问题