я пытаюсь заставить ввод и вывод работать одновременно, но без того, чтобы вывод не испортил это. Я заставил его работать с Windows, но не с Linux/Mac (для получения строки с терминала, чтобы я мог распечатать ее под выбранным выводимым текстом)
код:
#include <iostream>
#include <string>
#include <thread>
#include <atomic>
#include <chrono>
#if defined(_WIN32)
#include <windows.h>
#include <conio.h>
#elif defined(__unix__) || defined(__APPLE__)
//libraries needed
#endif
using namespace std;
void ReadCin(atomic<bool>& run)
{
string buffer;
while (run.load())
{
getline(cin, buffer);
if (buffer == "Quit")
{
run.store(false);
}
}
}
string get_terminal_line()
{
string line;
#if defined(_WIN32)
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO csbi;
if (GetConsoleScreenBufferInfo(hStdOut, &csbi))
{
DWORD dwSize = csbi.dwSize.X;
COORD cursorPos = csbi.dwCursorPosition;
cursorPos.X = 0;
SetConsoleCursorPosition(hStdOut, cursorPos);
for (DWORD i = 0; i < dwSize; ++i)
{
char ch;
DWORD charsRead;
if (ReadConsoleOutputCharacterA(hStdOut, &ch, 1, cursorPos, &charsRead))
{
line += ch;
cursorPos.X++;
}
else
{
break;
}
}
SetConsoleCursorPosition(hStdOut, csbi.dwCursorPosition);
}
#elif defined(__unix__) || defined(__APPLE__)
//here
#endif
size_t end = line.find_last_not_of(' ');
if (end != string::npos)
{
line.erase(end + 1);
}
else
{
line.clear();
}
return line;
}
void PrintNewLine(atomic<bool>& run)
{
while (run.load())
{
this_thread::sleep_for(chrono::seconds(2));
string line = get_terminal_line();
cout << "\33[2K\rNew line printed every 10 seconds" << endl;
bool is_space = true;
for (size_t length = 0; length != line.length(); length++)
{
if (isspace(line[length]) == false)
{
is_space = false;
break;
}
}
if (is_space == false)
{
cout << line;
}
}
}
int main()
{
atomic<bool> run(true);
thread cinThread(ReadCin, ref(run));
thread printThread(PrintNewLine, ref(run));
while (run.load())
{
//idk what to put here, so i'm just gonna leave it empty
}
run.store(false);
cinThread.join();
printThread.join();
return 0;
}
Я хочу, чтобы это работало с Mac и Linux (следовательно, макрос определен выше), который просто получает строку с терминала.
Если вы не хотите использовать ncurses, вы можете написать свои собственные функции, которые делают ровно столько, сколько нужно для работы ваших платформ. Это будет небольшое подмножество ncurses, поскольку ncurses работает на удивительно большом наборе терминалов. И тогда вы сможете обнаружить все функции, которые вам нужно создать (вместо того, чтобы они уже были написаны и отлажены в ncurses).
Я имею в виду, что большую часть работы я уже сделал, мне просто нужна помощь с Linux/Mac (поскольку я с ними менее знаком)
Ваш подход (чтение буфера консоли) не будет работать в средах, отличных от Windows. Извини. И, к сожалению, идея одновременного взаимодействия нескольких потоков с пользовательским терминалом также имеет проблемы и может вас укусить. Он просто не предназначен для такого использования. Вы просто хотите, чтобы в нижнем углу было что-то вроде часов или что-то в этом роде, пока пользователь (медленно) вводит данные в другом месте экрана?
Я просто хочу печатать текст на терминале без его печати поверх ранее набранной строки, чтобы пользователь мог продолжать вводить в него данные, как ncurses. Должен быть способ, если ncurses может это сделать. github.com/mirror/ncurses
ncurses довольно чистый и эффективный. Если бы я хотел знать, как сделать это правильно, не используя ncurses, я бы посмотрел, как ncurses выполняет эту работу. Скорее всего, если вы попытаетесь отойти слишком далеко от их подхода, вы чаще будете стрелять себе в ногу.
есть ли какие-либо предложения о том, как я могу сделать это на С++?
Моя система Windows в настоящее время не работает, поэтому я не могу запустить ваш код, чтобы точно увидеть, как он себя ведет. Расскажите нам, чего именно вы пытаетесь достичь визуально, и мы, возможно, сможем предложить что-то полезное. Однако если вам нужен необработанный ввод, скорее всего, лучше всего использовать NCurses.
Я не уверен, как я смогу объяснить это визуально, но я постараюсь изо всех сил. строка курсора (из терминала) сначала копируется и сохраняется перед печатью новой строки. Когда новая строка собирается быть напечатанной, она заменяет (строку курсора), сначала очищая строку курсора, а затем печатая себя на той же строке. тогда предыдущая строка курсора печатается после новой строки.
это останавливает печать, прерывая введенный текст в терминале, если в него не вносилось никаких изменений (если это все пробелы), то предыдущая строка не печатается
Похоже, вам нужна «оторванная» линия, которая используется для строк состояния (или ввода), в то время как другие строки выше (или ниже) могут продолжать прокручиваться?
хотите, чтобы я скомпилировал пример в x86, чтобы вы могли использовать Wine с терминалом в качестве примера, если вы используете Linux, извините, что неясно, не знаю, как это объяснить
@changethis1 для справки: ваша программа не будет работать в обычном Wine. Для этого понадобится wineconsole
, который эмулирует консоль Windows… с помощью ncurses.
я не использую ncurses...
о, ты имеешь в виду на винной консоли, разве это не вариант?
хмммммммммм Я тоже нашел это: stackoverflow.com/questions/55414228/…
вау, я только что понял, что могу взять его посимвольно, который набран, и сохранить его обратно в строку, а затем, когда это произойдет, просто перепечатать его снова для max/linux... Я думаю, это было возможно и легко
зачем все это было необходимо?!
в любом случае, я закончил код, спасибо за предложения...
В Linux вы управляете вводом/выводом терминала с помощью функции tcsetattr
, которая позволяет вам контролировать различные вещи, которые происходят в ядре/драйверах между вашей программой и терминалом.
Если я понимаю, о чем вы спрашиваете, вы хотите отключить автоматическое эхо ввода с клавиатуры, чтобы вы могли правильно позиционировать его при выводе. Вы делаете это с помощью флага ECHO
:
#include <termios.h>
:
struct termios term;
tcgetattr(0, &term); // get the setting on the terminal connected to stdin
term.c_lflag &= ~ECHO; // turn off the ECHO flag
tcsetattr(0, TCSANOW, &term);
Теперь ввод с клавиатуры будет «тихим» — не будет отображаться на экране, поэтому, если вы хотите, чтобы ввод был виден, вам придется вывести его самостоятельно. Если вы хотите иметь возможность получать (и, таким образом, отображать и видеть) нажатия клавиш до нажатия Enter, вам также необходимо очистить бит ICANON для канонической обработки ввода:
term.c_lflag &= ~(ECHO|ICANON); // turn off echo and canonical input
теперь каждое нажатие клавиши будет читабельно сразу после его нажатия, а драйвер терминала не будет обрабатывать символы возврата (вы получите настоящие символы возврата).
спасибо, я думаю, но я уже нашел решение, которое работает для меня
Если вы не хотите использовать ncurses, вам придется реализовать это самостоятельно... получайте удовольствие.