Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion Sample/App/Source/Main.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include <ctui_c.h>

#include "ctui_raw_mode.h"
Comment thread
Aintdev marked this conversation as resolved.
Resolved

#ifdef _WIN32
#include <windows.h>
#endif
Expand All @@ -13,7 +15,8 @@ int main() {
SetConsoleCP(CP_UTF8);
#endif

enable_raw_mode();
RawModeGuard rmg;
Comment thread
Aintdev marked this conversation as resolved.
Resolved

screen->config();

auto root = screen->make_child<VStack>(
Expand Down
1 change: 1 addition & 0 deletions Source/ctui.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@
#include "Widgets/Container/ctui_container_vstack.h"
#include "Widgets/Container/ctui_container_screen.h"
#include "ctui_utf8.h"
#include "ctui_raw_mode.h"
43 changes: 2 additions & 41 deletions Source/ctui_config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,51 +7,12 @@
#include <windows.h>
#include <conio.h>
#else
#include <termios.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <stdio.h>
#endif

namespace ctui {
bool raw_mode = false;


void enable_raw_mode() {
#ifdef _WIN32
HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
DWORD mode;
GetConsoleMode(hStdin, &mode);
SetConsoleMode(hStdin, mode & ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT));
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
GetConsoleMode(hOut, &mode);
SetConsoleMode(hOut, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
#else
termios t;
tcgetattr(STDIN_FILENO, &t);
t.c_lflag &= ~(ICANON | ECHO | IXON);
tcsetattr(STDIN_FILENO, TCSANOW, &t);
#endif
std::ios::sync_with_stdio(false);
raw_mode = true;
std::cout << "\033[2J\033[H";
}

void disable_raw_mode() {
#ifdef _WIN32
HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
DWORD mode;
GetConsoleMode(hStdin, &mode);
SetConsoleMode(hStdin, mode | ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT);
#else
termios t;
tcgetattr(STDIN_FILENO, &t);
t.c_lflag |= (ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &t);
#endif
raw_mode = false;
}

namespace ctui
{
std::pair<int, int> get_win_size() {
#ifdef _WIN32
CONSOLE_SCREEN_BUFFER_INFO csbi;
Expand Down
3 changes: 0 additions & 3 deletions Source/ctui_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,5 @@
#include <utility>

namespace ctui {
extern bool raw_mode;
void enable_raw_mode();
void disable_raw_mode();
std::pair<int, int> get_win_size();
}
61 changes: 61 additions & 0 deletions Source/ctui_raw_mode.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#include "pch.h"

#include "ctui_raw_mode.h"

namespace ctui
{
RawModeGuard::RawModeGuard()
{
#ifdef _WIN32
_stdin = GetStdHandle(STD_INPUT_HANDLE);
_stdout = GetStdHandle(STD_OUTPUT_HANDLE);

// GetConsoleMode fails if the handle is invalid or redirected (e.g. stdin/stdout
// piped to a file). Without this check, _original_*_mode stays uninitialized and
// we'd later restore garbage in the destructor.
if (!GetConsoleMode(_stdin, &_original_stdin_mode))
throw std::system_error(GetLastError(), std::system_category(), "GetConsoleMode (stdin) failed");
if (!GetConsoleMode(_stdout, &_original_stdout_mode))
throw std::system_error(GetLastError(), std::system_category(), "GetConsoleMode (stdout) failed");

DWORD stdin_mode = _original_stdin_mode;
stdin_mode &= ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT);
SetConsoleMode(_stdin, stdin_mode);

DWORD stdout_mode = _original_stdout_mode;
stdout_mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
SetConsoleMode(_stdout, stdout_mode);
#else
// Same reasoning as GetConsoleMode above: tcgetattr fails for non-TTY stdin
// (redirected/piped), and leaves _original untouched on failure.
if (tcgetattr(STDIN_FILENO, &_original) != 0)
throw std::system_error(errno, std::generic_category(), "tcgetattr failed");

termios raw = _original;
raw.c_lflag &= ~(ICANON | ECHO);
raw.c_iflag &= ~IXON;

tcsetattr(STDIN_FILENO, TCSANOW, &raw);
#endif

// TODO: Move code to main loop as soon as it exists.
std::ios::sync_with_stdio(false);
std::cout << "\033[?1049h\033[2J\033[H";
}

RawModeGuard::~RawModeGuard()
{
// Must run *before* restoring the console mode below: if the original stdout
// mode didn't have ENABLE_VIRTUAL_TERMINAL_PROCESSING (the common case), the
// escape sequence would no longer be interpreted once we restore it, and the
// terminal would be stuck in the alternate screen buffer.
std::cout << "\033[?1049l"; // TODO: move to main loop

#ifdef _WIN32
SetConsoleMode(_stdin, _original_stdin_mode);
SetConsoleMode(_stdout, _original_stdout_mode);
#else
tcsetattr(STDIN_FILENO, TCSANOW, &_original);
#endif
}
}
33 changes: 33 additions & 0 deletions Source/ctui_raw_mode.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#pragma once

#ifdef _WIN32
#include <windows.h>
#else
#include <termios.h>
#include <unistd.h>
#endif

namespace ctui
{
class RawModeGuard
Comment thread
Aintdev marked this conversation as resolved.
Resolved
{
// Original Terminal State
#ifdef _WIN32
HANDLE _stdin;
HANDLE _stdout;

DWORD _original_stdin_mode;
DWORD _original_stdout_mode;
#else
termios _original;
#endif
public:
RawModeGuard();
~RawModeGuard();

RawModeGuard(const RawModeGuard&) = delete;
RawModeGuard& operator=(const RawModeGuard&) = delete;
RawModeGuard(RawModeGuard&&) = delete;
RawModeGuard& operator=(RawModeGuard&&) = delete;
};
}