Skip to content

Commit

Permalink
Raw/Cooked (#323)
Browse files Browse the repository at this point in the history
* better raw/cooked mode handling
  • Loading branch information
flagarde authored Oct 27, 2023
1 parent 285ca28 commit c357419
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 57 deletions.
2 changes: 2 additions & 0 deletions cpp-terminal/iostream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

#include "cpp-terminal/iostream.hpp"

#include "cpp-terminal/buffer.hpp"

#include <iostream>
#include <new>

Expand Down
131 changes: 76 additions & 55 deletions cpp-terminal/platforms/terminal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,20 @@

#include "cpp-terminal/terminal.hpp"

#include "cpp-terminal/exception.hpp"
#include "cpp-terminal/platforms/env.hpp"
#include "cpp-terminal/platforms/exception.hpp"
#include "cpp-terminal/platforms/file.hpp"

#ifdef _WIN32
#include <io.h>
#include <windows.h>
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
#if !defined(ENABLE_VIRTUAL_TERMINAL_PROCESSING)
#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
#endif
#ifndef DISABLE_NEWLINE_AUTO_RETURN
#if !defined(DISABLE_NEWLINE_AUTO_RETURN)
#define DISABLE_NEWLINE_AUTO_RETURN 0x0008
#endif
#ifndef ENABLE_VIRTUAL_TERMINAL_INPUT
#if !defined(ENABLE_VIRTUAL_TERMINAL_INPUT)
#define ENABLE_VIRTUAL_TERMINAL_INPUT 0x0200
#endif
#else
Expand All @@ -37,18 +37,16 @@ void Term::Terminal::set_unset_utf8()
static UINT in_code_page{0};
if(!enabled)
{
out_code_page = GetConsoleOutputCP();
if(out_code_page == 0) throw Term::Exception("GetConsoleOutputCP() failed");
if(!SetConsoleOutputCP(CP_UTF8)) throw Term::Exception("SetConsoleOutputCP(CP_UTF8) failed");
in_code_page = GetConsoleCP();
if(out_code_page == 0) throw Term::Exception("GetConsoleCP() failed");
if(!SetConsoleCP(CP_UTF8)) throw Term::Exception("SetConsoleCP(CP_UTF8) failed");
if((out_code_page = GetConsoleOutputCP()) == 0) throw Term::Private::WindowsError(GetLastError());
if(!SetConsoleOutputCP(CP_UTF8)) throw Term::Private::WindowsError(GetLastError());
if((in_code_page = GetConsoleCP()) == 0) throw Term::Private::WindowsError(GetLastError());
if(!SetConsoleCP(CP_UTF8)) throw Term::Private::WindowsError(GetLastError());
enabled = true;
}
else
{
if(!SetConsoleOutputCP(out_code_page)) throw Term::Exception("SetConsoleOutputCP(out_code_page) failed");
if(!SetConsoleCP(in_code_page)) throw Term::Exception("SetConsoleCP(in_code_page) failed");
if(!SetConsoleOutputCP(out_code_page)) throw Term::Private::WindowsError(GetLastError());
if(!SetConsoleCP(in_code_page)) throw Term::Private::WindowsError(GetLastError());
}
#else
if(!enabled)
Expand All @@ -64,35 +62,34 @@ void Term::Terminal::set_unset_utf8()
#endif
}

///
///@brief Store and restore the default state of the terminal. Configure the default mode for cpp-terminal.
///
void Term::Terminal::store_and_restore()
{
static bool enabled{false};
#ifdef _WIN32
static DWORD dwOriginalOutMode{0};
static DWORD dwOriginalInMode{0};
#if defined(_WIN32)
static DWORD originalOut{0};
static DWORD originalIn{0};
if(!enabled)
{
if(!Private::out.null())
{
if(GetConsoleMode(Private::out.handle(), &dwOriginalOutMode) == 0) { throw Term::Exception("GetConsoleMode() failed"); }
if(GetConsoleMode(Private::in.handle(), &dwOriginalInMode) == 0) { throw Term::Exception("GetConsoleMode() failed"); }
}
DWORD in{(dwOriginalInMode & ~ENABLE_QUICK_EDIT_MODE) | (ENABLE_EXTENDED_FLAGS | activateFocusEvents() | activateMouseEvents())};
DWORD out{dwOriginalOutMode};
if(GetConsoleMode(Private::out.handle(), &originalOut) == 0) { throw Term::Private::WindowsError(GetLastError()); }
if(GetConsoleMode(Private::in.handle(), &originalIn) == 0) { throw Term::Private::WindowsError(GetLastError()); }
DWORD in{(originalIn & ~ENABLE_QUICK_EDIT_MODE) | (ENABLE_EXTENDED_FLAGS | activateFocusEvents() | activateMouseEvents())};
DWORD out{originalOut};
if(!m_terminfo.isLegacy())
{
out |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN;
in |= ENABLE_VIRTUAL_TERMINAL_INPUT;
}
if(!SetConsoleMode(Private::out.handle(), out)) { throw Term::Exception("SetConsoleMode() failed in destructor"); }
if(!SetConsoleMode(Private::in.handle(), in)) { throw Term::Exception("SetConsoleMode() failed"); }
if(!SetConsoleMode(Private::out.handle(), out)) { throw Term::Private::WindowsError(GetLastError()); }
if(!SetConsoleMode(Private::in.handle(), in)) { throw Term::Private::WindowsError(GetLastError()); }
enabled = true;
}
else
{
if(!SetConsoleMode(Private::out.handle(), dwOriginalOutMode)) { throw Term::Exception("SetConsoleMode() failed in destructor"); }
if(!SetConsoleMode(Private::in.handle(), dwOriginalInMode)) { throw Term::Exception("SetConsoleMode() failed in destructor"); }
enabled = false;
if(!SetConsoleMode(Private::out.handle(), originalOut)) { throw Term::Private::WindowsError(GetLastError()); }
if(!SetConsoleMode(Private::in.handle(), originalIn)) { throw Term::Private::WindowsError(GetLastError()); }
}
#else
static termios orig_termios;
Expand All @@ -101,19 +98,16 @@ void Term::Terminal::store_and_restore()
if(!Private::out.null())
if(tcgetattr(Private::out.fd(), &orig_termios) == -1) { throw Term::Exception("tcgetattr() failed"); }
termios term = orig_termios;
term.c_cflag &= ~PARENB; // Clear parity bit, disabling parity (most common)
term.c_cflag &= ~CSTOPB; // Clear stop field, only one stop bit used in communication (most common)
term.c_cflag &= ~CSIZE; // Clear all the size bits, then use one of the statements below
term.c_cflag |= CS8; // 8 bits per byte (most common)
if(!Private::out.null())
if(tcsetattr(Private::out.fd(), TCSAFLUSH, &term) == -1) { throw Term::Exception("tcsetattr() failed in destructor"); }
enabled = true;
}
else
{
desactivateMouseEvents();
desactivateFocusEvents();
if(!Private::out.null())
if(tcsetattr(Private::out.fd(), TCSAFLUSH, &orig_termios) == -1) { throw Term::Exception("tcsetattr() failed in destructor"); }
enabled = false;
}
#endif
}
Expand All @@ -132,7 +126,7 @@ int Term::Terminal::desactivateMouseEvents()
#if defined(_WIN32)
return ENABLE_MOUSE_INPUT;
#else
return Term::Private::out.write("\033[?1003l\033[?1006l");
return Term::Private::out.write("\033[?1006l\033[?1003l\033[?1002l");
#endif
}

Expand Down Expand Up @@ -167,34 +161,61 @@ void Term::Terminal::setBadStateReturnCode()
}
}

void Term::Terminal::setRawMode()
///
///@brief Set mode raw/cooked.
///First call is to save the good state set-up by cpp-terminal.
///
void Term::Terminal::setMode()
{
static bool activated{false};
#if defined(_WIN32)
DWORD flags{0};
if(!Private::out.null())
if(!GetConsoleMode(Private::in.handle(), &flags)) { throw Term::Exception("GetConsoleMode() failed"); }
if(m_options.has(Option::NoSignalKeys)) { flags &= ~ENABLE_PROCESSED_INPUT; }
flags &= ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT);
static DWORD flags{0};
if(!activated)
{
if(!Private::out.null())
if(!GetConsoleMode(Private::in.handle(), &flags)) { throw Term::Private::WindowsError(GetLastError()); }
activated = true;
}
DWORD send = flags;
if(m_options.has(Option::Raw)) { send &= ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT); }
else if(m_options.has(Option::Cooked)) { send |= (ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT); }
if(m_options.has(Option::NoSignalKeys)) { send &= ~ENABLE_PROCESSED_INPUT; }
else if(m_options.has(Option::SignalKeys)) { send |= ENABLE_PROCESSED_INPUT; }
if(!Private::out.null())
if(!SetConsoleMode(Private::in.handle(), flags)) { throw Term::Exception("SetConsoleMode() failed"); }
if(!SetConsoleMode(Private::in.handle(), send)) { throw Term::Private::WindowsError(GetLastError()); }
#else
if(!Private::out.null())
{
::termios raw;
if(tcgetattr(Private::out.fd(), &raw) == -1) { throw Term::Exception("tcgetattr() failed"); }
// Put terminal in raw mode
raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
// This disables output post-processing, requiring explicit \r\n. We
// keep it enabled, so that in C++, one can still just use std::endl
// for EOL instead of "\r\n".
// raw.c_oflag &= ~(OPOST);
raw.c_lflag &= ~(ECHO | ICANON | IEXTEN);
if(m_options.has(Option::NoSignalKeys)) { raw.c_lflag &= ~ISIG; }
raw.c_cc[VMIN] = 1;
raw.c_cc[VTIME] = 0;
if(tcsetattr(Private::out.fd(), TCSAFLUSH, &raw) == -1) { throw Term::Exception("tcsetattr() failed"); }
activateMouseEvents();
activateFocusEvents();
static ::termios raw;
if(!activated)
{
if(tcgetattr(Private::out.fd(), &raw) == -1) { throw Term::Exception("tcgetattr() failed"); }
activated = true;
}
::termios send = raw;
if(m_options.has(Option::Raw))
{
// Put terminal in raw mode
send.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
// This disables output post-processing, requiring explicit \r\n. We
// keep it enabled, so that in C++, one can still just use std::endl
// for EOL instead of "\r\n".
// raw.c_oflag &= ~(OPOST);
send.c_lflag &= ~(ECHO | ICANON | IEXTEN);
send.c_cc[VMIN] = 1;
send.c_cc[VTIME] = 0;
activateMouseEvents();
activateFocusEvents();
}
else if(m_options.has(Option::Cooked))
{
send = raw;
desactivateMouseEvents();
desactivateFocusEvents();
}
if(m_options.has(Option::NoSignalKeys)) { send.c_lflag &= ~ISIG; } //FIXME need others flags !
else if(m_options.has(Option::NoSignalKeys)) { send.c_lflag |= ISIG; }
if(tcsetattr(Private::out.fd(), TCSAFLUSH, &send) == -1) { throw Term::Exception("tcsetattr() failed"); }
}
#endif
}
3 changes: 2 additions & 1 deletion cpp-terminal/terminal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ Term::Terminal::Terminal()
setBadStateReturnCode();
Term::Private::m_fileInitializer.init();
store_and_restore();
setMode(); //Save the default cpp-terminal mode done in store_and_restore();
set_unset_utf8();
m_terminfo.checkUTF8();
}
Expand Down Expand Up @@ -85,7 +86,7 @@ void Term::Terminal::applyOptions()
{
if(m_options.has(Option::ClearScreen)) Term::Private::out.write(screen_save() + clear_buffer() + style(Style::Reset) + cursor_move(1, 1));
if(m_options.has(Option::NoCursor)) Term::Private::out.write(cursor_off());
if(m_options.has(Option::Raw)) setRawMode();
setMode();
}

std::string Term::terminal_title(const std::string& title) { return "\033]0;" + title + '\a'; }
Expand Down
2 changes: 1 addition & 1 deletion cpp-terminal/terminal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class Terminal
void setBadStateReturnCode();
void setOptions();
void applyOptions();
void setRawMode();
void setMode();
int activateMouseEvents();
int desactivateMouseEvents();
int activateFocusEvents();
Expand Down

0 comments on commit c357419

Please sign in to comment.