kernel+libc+sh: Make the TTY device actually follow termios rules
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
Like, so much more termios compatibility!
This commit is contained in:
parent
efd5bae7a5
commit
de6fe7f7c2
@ -98,7 +98,11 @@ Result<int> luna_main(int argc, char** argv)
|
||||
auto maybe_cmd = input_file->read_line();
|
||||
if (maybe_cmd.has_error())
|
||||
{
|
||||
if (maybe_cmd.error() == EINTR) continue;
|
||||
if (maybe_cmd.error() == EINTR)
|
||||
{
|
||||
os::println("");
|
||||
continue;
|
||||
}
|
||||
return maybe_cmd.release_error();
|
||||
}
|
||||
|
||||
@ -150,6 +154,8 @@ Result<int> luna_main(int argc, char** argv)
|
||||
{
|
||||
int sig = WTERMSIG(status);
|
||||
if (sig != SIGINT && sig != SIGQUIT) os::println("[sh] Process %d exited: %s", child, strsignal(sig));
|
||||
else
|
||||
os::println("");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,13 +3,14 @@
|
||||
|
||||
namespace Keyboard
|
||||
{
|
||||
enum Modifiers
|
||||
struct KeyboardState
|
||||
{
|
||||
LeftControl = 1,
|
||||
LeftAlt = 2,
|
||||
bool ignore_next { false };
|
||||
bool left_shift { false };
|
||||
bool right_shift { false };
|
||||
bool left_control { false };
|
||||
bool capslock { false };
|
||||
};
|
||||
|
||||
Option<char> decode_scancode(u8 scancode);
|
||||
|
||||
int modifiers();
|
||||
Option<char> decode_scancode_tty(u8 scancode, KeyboardState& state);
|
||||
}
|
||||
|
@ -147,8 +147,7 @@ void io_thread()
|
||||
u8 scancode;
|
||||
while (!scancode_queue.try_pop(scancode)) kernel_wait_for_event();
|
||||
|
||||
char key;
|
||||
if (Keyboard::decode_scancode(scancode).try_set_value(key)) ConsoleDevice::did_press_key(key);
|
||||
ConsoleDevice::did_press_or_release_key(scancode);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "arch/Keyboard.h"
|
||||
#include <luna/CType.h>
|
||||
|
||||
// PS/2 keyboard decoding routine.
|
||||
|
||||
@ -28,11 +29,6 @@ static bool should_ignore_key(u8 scancode)
|
||||
return (scancode > 0x3A && scancode < 0x47) || scancode == TAB || scancode == F11 || scancode == F12;
|
||||
}
|
||||
|
||||
static bool g_ignore_next { false };
|
||||
static bool g_left_shift { false };
|
||||
static bool g_right_shift { false };
|
||||
static bool g_capslock { false };
|
||||
|
||||
constexpr char key_table[] = {
|
||||
'\0',
|
||||
'\1', // escape
|
||||
@ -117,28 +113,26 @@ constexpr char shifted_key_table[] = {
|
||||
'.', // keypad .
|
||||
};
|
||||
|
||||
static bool is_shifted()
|
||||
static bool is_shifted(const Keyboard::KeyboardState& state)
|
||||
{
|
||||
if (g_capslock) return !(g_left_shift || g_right_shift);
|
||||
return g_left_shift || g_right_shift;
|
||||
if (state.capslock) return !(state.left_shift || state.right_shift);
|
||||
return state.left_shift || state.right_shift;
|
||||
}
|
||||
|
||||
int g_modifiers = 0;
|
||||
|
||||
namespace Keyboard
|
||||
{
|
||||
Option<char> decode_scancode(u8 scancode)
|
||||
Option<char> decode_scancode_tty(u8 scancode, KeyboardState& state)
|
||||
{
|
||||
if (g_ignore_next)
|
||||
if (state.ignore_next)
|
||||
{
|
||||
g_ignore_next = false;
|
||||
state.ignore_next = false;
|
||||
return {};
|
||||
}
|
||||
|
||||
// FIXME: Support extended scancodes.
|
||||
if (scancode == EXTENDED_KEY_CODE)
|
||||
{
|
||||
g_ignore_next = true;
|
||||
state.ignore_next = true;
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -146,35 +140,27 @@ namespace Keyboard
|
||||
|
||||
if (scancode == LEFT_SHIFT)
|
||||
{
|
||||
g_left_shift = !released;
|
||||
state.left_shift = !released;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (scancode == RIGHT_SHIFT)
|
||||
{
|
||||
g_right_shift = !released;
|
||||
state.right_shift = !released;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (scancode == CAPS_LOCK)
|
||||
{
|
||||
if (!released) g_capslock = !g_capslock;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (scancode == LEFT_ALT)
|
||||
{
|
||||
if (released) g_modifiers &= ~LeftAlt;
|
||||
else
|
||||
g_modifiers |= LeftAlt;
|
||||
if (!released) state.capslock = !state.capslock;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (scancode == LEFT_CONTROL)
|
||||
{
|
||||
if (released) g_modifiers &= ~LeftControl;
|
||||
if (released) state.left_control = false;
|
||||
else
|
||||
g_modifiers |= LeftControl;
|
||||
state.left_control = true;
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -182,12 +168,20 @@ namespace Keyboard
|
||||
|
||||
if (released) return {};
|
||||
|
||||
if (is_shifted()) return shifted_key_table[scancode];
|
||||
if (state.left_control)
|
||||
{
|
||||
char key;
|
||||
if (is_shifted(state)) key = shifted_key_table[scancode];
|
||||
else
|
||||
key = key_table[scancode];
|
||||
if (_islower(key)) key = (char)_toupper(key);
|
||||
if (_isupper(key)) return key - 0x40;
|
||||
if (key == '@') return key - 0x40;
|
||||
if (key > 'Z' && key < '`') return key - 0x40;
|
||||
if (key == '?') return 0x7f;
|
||||
}
|
||||
|
||||
if (is_shifted(state)) return shifted_key_table[scancode];
|
||||
return key_table[scancode];
|
||||
}
|
||||
|
||||
int modifiers()
|
||||
{
|
||||
return g_modifiers;
|
||||
}
|
||||
}
|
||||
|
@ -1,36 +1,34 @@
|
||||
#include "fs/devices/ConsoleDevice.h"
|
||||
#include "Log.h"
|
||||
#include "arch/Keyboard.h"
|
||||
#include "fs/devices/DeviceRegistry.h"
|
||||
#include "memory/MemoryManager.h"
|
||||
#include "thread/Scheduler.h"
|
||||
#include "video/TextConsole.h"
|
||||
#include <bits/ioctl-defs.h>
|
||||
#include <bits/termios.h>
|
||||
#include <luna/Buffer.h>
|
||||
#include <luna/CString.h>
|
||||
#include <luna/CType.h>
|
||||
#include <luna/Units.h>
|
||||
#include <luna/Vector.h>
|
||||
|
||||
static Buffer g_console_input;
|
||||
static bool g_eof { false };
|
||||
static bool g_echo { true };
|
||||
static bool g_stop_background_output { false };
|
||||
|
||||
static Option<pid_t> g_foreground_process_group;
|
||||
Vector<SharedPtr<ConsoleDevice>> ConsoleDevice::m_console_devices;
|
||||
|
||||
Result<void> ConsoleDevice::create()
|
||||
{
|
||||
auto device = (SharedPtr<Device>)TRY(make_shared<ConsoleDevice>());
|
||||
g_foreground_process_group = {};
|
||||
auto device = TRY(make_shared<ConsoleDevice>());
|
||||
device->m_settings.c_lflag = ECHO | ECHOE | ECHOCTL | ISIG | ICANON;
|
||||
device->m_settings.c_cc[VEOF] = '\4';
|
||||
device->m_settings.c_cc[VERASE] = '\b';
|
||||
device->m_settings.c_cc[VINTR] = '\3';
|
||||
device->m_settings.c_cc[VQUIT] = '\x1c';
|
||||
TRY(m_console_devices.try_append(device));
|
||||
return DeviceRegistry::register_special_device(DeviceRegistry::Console, 0, device);
|
||||
}
|
||||
|
||||
static Result<void> handle_background_process_group(bool can_succeed, int signo)
|
||||
Result<void> ConsoleDevice::handle_background_process_group(bool can_succeed, int signo) const
|
||||
{
|
||||
if (!g_foreground_process_group.has_value()) return {};
|
||||
if (!m_foreground_process_group.has_value()) return {};
|
||||
|
||||
auto foreground_pgrp = g_foreground_process_group.value();
|
||||
auto foreground_pgrp = m_foreground_process_group.value();
|
||||
|
||||
auto* current = Scheduler::current();
|
||||
if ((pid_t)current->pgid == foreground_pgrp) return {};
|
||||
@ -51,22 +49,22 @@ Result<usize> ConsoleDevice::read(u8* buf, usize, usize length) const
|
||||
{
|
||||
TRY(handle_background_process_group(false, SIGTTIN));
|
||||
|
||||
if (length > g_console_input.size()) length = g_console_input.size();
|
||||
if (length > m_input_buffer.size()) length = m_input_buffer.size();
|
||||
|
||||
memcpy(buf, g_console_input.data(), length);
|
||||
memcpy(buf, m_input_buffer.data(), length);
|
||||
|
||||
memmove(g_console_input.data(), g_console_input.data() + length, g_console_input.size() - length);
|
||||
memmove(m_input_buffer.data(), m_input_buffer.data() + length, m_input_buffer.size() - length);
|
||||
|
||||
g_console_input.try_resize(g_console_input.size() - length).release_value();
|
||||
m_input_buffer.try_resize(m_input_buffer.size() - length).release_value();
|
||||
|
||||
if (!length && g_eof) g_eof = false;
|
||||
if (!length && m_may_read_without_blocking) m_may_read_without_blocking = false;
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
Result<usize> ConsoleDevice::write(const u8* buf, usize, usize length)
|
||||
{
|
||||
if (g_stop_background_output) TRY(handle_background_process_group(true, SIGTTOU));
|
||||
if (m_settings.c_lflag & TOSTOP) TRY(handle_background_process_group(true, SIGTTOU));
|
||||
|
||||
TextConsole::write((const char*)buf, length);
|
||||
return length;
|
||||
@ -74,78 +72,124 @@ Result<usize> ConsoleDevice::write(const u8* buf, usize, usize length)
|
||||
|
||||
bool ConsoleDevice::blocking() const
|
||||
{
|
||||
return g_eof ? false : g_console_input.size() == 0;
|
||||
return m_may_read_without_blocking ? false : m_input_buffer.size() == 0;
|
||||
}
|
||||
|
||||
static Vector<u8> g_temp_input;
|
||||
|
||||
void ConsoleDevice::did_press_key(char key)
|
||||
void ConsoleDevice::did_press_or_release_key(u8 scancode)
|
||||
{
|
||||
if (key == '\b')
|
||||
for (const auto& device : m_console_devices) { device->process_key_event(scancode); }
|
||||
}
|
||||
|
||||
void ConsoleDevice::process_key_event(u8 scancode)
|
||||
{
|
||||
auto rc = Keyboard::decode_scancode_tty(scancode, m_kb_state);
|
||||
if (!rc.has_value()) return;
|
||||
char key = rc.value();
|
||||
check(key >= 0);
|
||||
|
||||
bool is_special_character { false };
|
||||
|
||||
if (is_canonical())
|
||||
{
|
||||
if (g_temp_input.try_pop().has_value())
|
||||
if (key == m_settings.c_cc[VERASE])
|
||||
{
|
||||
if (g_echo) TextConsole::putwchar(L'\b');
|
||||
auto maybe_char = m_line_buffer.try_pop();
|
||||
if (maybe_char.has_value() && maybe_char.value())
|
||||
{
|
||||
if ((m_settings.c_lflag & ECHO) && (m_settings.c_lflag & ECHOE))
|
||||
{
|
||||
TextConsole::putwchar(L'\b');
|
||||
if (_iscntrl(maybe_char.value())) TextConsole::putwchar(L'\b');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ((m_settings.c_lflag & ECHOE)) return;
|
||||
else
|
||||
is_special_character = true;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Ctrl+D
|
||||
if (key == 'd' && (Keyboard::modifiers() == Keyboard::LeftControl))
|
||||
{
|
||||
if (g_temp_input.size() == 0) g_eof = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (key == 'c' && (Keyboard::modifiers() == Keyboard::LeftControl))
|
||||
{
|
||||
g_temp_input.clear();
|
||||
|
||||
if (g_echo) TextConsole::wprintln(L"^C");
|
||||
|
||||
if (g_foreground_process_group.has_value())
|
||||
if (key == m_settings.c_cc[VEOF])
|
||||
{
|
||||
Scheduler::for_each_in_process_group(g_foreground_process_group.value(), [](Thread* thread) {
|
||||
thread->send_signal(SIGINT);
|
||||
return true;
|
||||
});
|
||||
m_input_buffer.append_data(m_line_buffer.data(), m_line_buffer.size());
|
||||
m_line_buffer.clear();
|
||||
|
||||
m_may_read_without_blocking = true;
|
||||
is_special_character = true;
|
||||
}
|
||||
|
||||
return;
|
||||
if (m_settings.c_lflag & ISIG)
|
||||
{
|
||||
if (key == m_settings.c_cc[VINTR])
|
||||
{
|
||||
if (!(m_settings.c_lflag & NOFLSH)) m_line_buffer.clear();
|
||||
|
||||
if (m_foreground_process_group.has_value())
|
||||
{
|
||||
Scheduler::for_each_in_process_group(m_foreground_process_group.value(), [](Thread* thread) {
|
||||
thread->send_signal(SIGINT);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
is_special_character = true;
|
||||
}
|
||||
|
||||
if (key == m_settings.c_cc[VQUIT])
|
||||
{
|
||||
if (!(m_settings.c_lflag & NOFLSH)) m_line_buffer.clear();
|
||||
|
||||
if (m_foreground_process_group.has_value())
|
||||
{
|
||||
Scheduler::for_each_in_process_group(m_foreground_process_group.value(), [](Thread* thread) {
|
||||
thread->send_signal(SIGQUIT);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
is_special_character = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (key == 'e' && (Keyboard::modifiers() == (Keyboard::LeftAlt | Keyboard::LeftControl)))
|
||||
if (!is_special_character)
|
||||
{
|
||||
Scheduler::dump_state();
|
||||
return;
|
||||
if (is_canonical())
|
||||
{
|
||||
m_line_buffer.try_append((u8)key);
|
||||
|
||||
if (key == '\n')
|
||||
{
|
||||
m_input_buffer.append_data(m_line_buffer.data(), m_line_buffer.size());
|
||||
m_line_buffer.clear();
|
||||
}
|
||||
}
|
||||
else
|
||||
m_input_buffer.append_data((u8*)&key, 1);
|
||||
}
|
||||
|
||||
if (key == 'm' && (Keyboard::modifiers() == (Keyboard::LeftAlt | Keyboard::LeftControl)))
|
||||
if (!(m_settings.c_lflag & ECHO)) return;
|
||||
|
||||
if (_iscntrl(key))
|
||||
{
|
||||
kinfoln("Total memory: %s", to_dynamic_unit(MemoryManager::total()).release_value().chars());
|
||||
kinfoln("Free memory: %s", to_dynamic_unit(MemoryManager::free()).release_value().chars());
|
||||
kinfoln("Used memory: %s", to_dynamic_unit(MemoryManager::used()).release_value().chars());
|
||||
kinfoln("Reserved memory: %s", to_dynamic_unit(MemoryManager::reserved()).release_value().chars());
|
||||
return;
|
||||
if (m_settings.c_lflag & ECHOCTL)
|
||||
{
|
||||
bool should_echo = true;
|
||||
if (key == '\n' || key == '\0' || key == m_settings.c_cc[VEOF]) should_echo = false;
|
||||
|
||||
if (should_echo)
|
||||
{
|
||||
char caret_notation[3] = { '^', '\0', '\0' };
|
||||
if (key == 0x7f) caret_notation[1] = '?';
|
||||
else
|
||||
caret_notation[1] = key + 0x40;
|
||||
|
||||
TextConsole::print(caret_notation);
|
||||
}
|
||||
else
|
||||
TextConsole::putchar(key);
|
||||
}
|
||||
}
|
||||
|
||||
if (key == 'h' && (Keyboard::modifiers() == (Keyboard::LeftAlt | Keyboard::LeftControl)))
|
||||
{
|
||||
dump_heap_usage();
|
||||
return;
|
||||
}
|
||||
|
||||
g_temp_input.try_append((u8)key);
|
||||
|
||||
if (key == '\n')
|
||||
{
|
||||
g_console_input.append_data(g_temp_input.data(), g_temp_input.size());
|
||||
g_temp_input.clear();
|
||||
}
|
||||
|
||||
if (!g_echo) return;
|
||||
TextConsole::putchar(key);
|
||||
else
|
||||
TextConsole::putchar(key);
|
||||
}
|
||||
|
||||
Result<u64> ConsoleDevice::ioctl(int request, void* arg)
|
||||
@ -153,27 +197,13 @@ Result<u64> ConsoleDevice::ioctl(int request, void* arg)
|
||||
switch (request)
|
||||
{
|
||||
case TCGETS: {
|
||||
struct termios tc
|
||||
{
|
||||
.c_lflag = 0
|
||||
};
|
||||
|
||||
if (g_echo) tc.c_lflag |= ECHO;
|
||||
if (g_stop_background_output) tc.c_lflag |= TOSTOP;
|
||||
return MemoryManager::copy_to_user_typed((struct termios*)arg, &tc) ? 0 : err(EFAULT);
|
||||
return MemoryManager::copy_to_user_typed((struct termios*)arg, &m_settings) ? 0 : err(EFAULT);
|
||||
}
|
||||
case TCSETS: {
|
||||
TRY(handle_background_process_group(true, SIGTTOU));
|
||||
|
||||
struct termios tc;
|
||||
if (!MemoryManager::copy_from_user_typed((const struct termios*)arg, &tc)) return err(EFAULT);
|
||||
if (tc.c_lflag & ECHO) g_echo = true;
|
||||
else
|
||||
g_echo = false;
|
||||
|
||||
if (tc.c_lflag & TOSTOP) g_stop_background_output = true;
|
||||
else
|
||||
g_stop_background_output = false;
|
||||
if (!MemoryManager::copy_from_user_typed((const struct termios*)arg, &m_settings)) return err(EFAULT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -190,11 +220,11 @@ Result<u64> ConsoleDevice::ioctl(int request, void* arg)
|
||||
});
|
||||
if (!pgid_exists) return err(EPERM);
|
||||
|
||||
g_foreground_process_group = pgid;
|
||||
m_foreground_process_group = pgid;
|
||||
return 0;
|
||||
}
|
||||
case TIOCGPGRP: {
|
||||
pid_t pgid = g_foreground_process_group.value_or((pid_t)next_thread_id());
|
||||
pid_t pgid = m_foreground_process_group.value_or((pid_t)next_thread_id());
|
||||
if (!MemoryManager::copy_to_user_typed((pid_t*)arg, &pgid)) return err(EFAULT);
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,5 +1,8 @@
|
||||
#pragma once
|
||||
#include "arch/Keyboard.h"
|
||||
#include "fs/devices/DeviceRegistry.h"
|
||||
#include <bits/termios.h>
|
||||
#include <luna/Buffer.h>
|
||||
|
||||
class ConsoleDevice : public Device
|
||||
{
|
||||
@ -11,7 +14,7 @@ class ConsoleDevice : public Device
|
||||
|
||||
Result<usize> write(const u8*, usize, usize) override;
|
||||
|
||||
static void did_press_key(char key);
|
||||
static void did_press_or_release_key(u8 scancode);
|
||||
|
||||
bool blocking() const override;
|
||||
|
||||
@ -28,4 +31,24 @@ class ConsoleDevice : public Device
|
||||
}
|
||||
|
||||
virtual ~ConsoleDevice() = default;
|
||||
|
||||
private:
|
||||
struct termios m_settings;
|
||||
mutable Buffer m_input_buffer;
|
||||
Option<pid_t> m_foreground_process_group {};
|
||||
Vector<u8> m_line_buffer;
|
||||
mutable Keyboard::KeyboardState m_kb_state;
|
||||
|
||||
static Vector<SharedPtr<ConsoleDevice>> m_console_devices;
|
||||
|
||||
void process_key_event(u8 scancode);
|
||||
|
||||
mutable bool m_may_read_without_blocking { false };
|
||||
|
||||
inline bool is_canonical() const
|
||||
{
|
||||
return m_settings.c_lflag & ICANON;
|
||||
}
|
||||
|
||||
Result<void> handle_background_process_group(bool can_succeed, int signo) const;
|
||||
};
|
||||
|
@ -101,6 +101,10 @@ namespace TextConsole
|
||||
if (should_scroll()) scroll();
|
||||
break;
|
||||
}
|
||||
case L'\t': {
|
||||
wprint(L" ");
|
||||
break;
|
||||
}
|
||||
case L'\r': g_x_position = 0; break;
|
||||
case L'\b':
|
||||
if (g_x_position != 0)
|
||||
@ -210,11 +214,11 @@ namespace TextConsole
|
||||
|
||||
u16 rows()
|
||||
{
|
||||
return Framebuffer::height() / FONT_HEIGHT;
|
||||
return (u16)Framebuffer::height() / FONT_HEIGHT;
|
||||
}
|
||||
|
||||
u16 cols()
|
||||
{
|
||||
return Framebuffer::width() / FONT_WIDTH;
|
||||
return (u16)Framebuffer::width() / FONT_WIDTH;
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
#ifndef _BITS_FIXED_SIZE_TYPES_H
|
||||
#define _BITS_FIXED_SIZE_TYPES_H
|
||||
|
||||
#if !defined(_SYS_TYPES_H) && !defined(_BITS_SETJMP_TYPES_H) && !defined(_BITS_MAKEDEV_H)
|
||||
#if !defined(_SYS_TYPES_H) && !defined(_BITS_SETJMP_TYPES_H) && !defined(_BITS_MAKEDEV_H) && !defined(_BITS_TERMIOS_H)
|
||||
#error "Never include bits/fixed-size-types.h, use the standard <stdint.h> header instead."
|
||||
#endif
|
||||
|
||||
|
@ -3,25 +3,41 @@
|
||||
#ifndef _BITS_TERMIOS_H
|
||||
#define _BITS_TERMIOS_H
|
||||
|
||||
#include <bits/fixed-size-types.h>
|
||||
|
||||
typedef long tcflag_t;
|
||||
typedef char cc_t;
|
||||
|
||||
#define NCCS 4
|
||||
|
||||
#define VEOF 0
|
||||
#define VERASE 1
|
||||
#define VINTR 2
|
||||
#define VQUIT 3
|
||||
|
||||
// VERY incomplete termios structure.
|
||||
struct termios
|
||||
{
|
||||
tcflag_t c_lflag;
|
||||
cc_t c_cc[NCCS];
|
||||
};
|
||||
|
||||
struct winsize
|
||||
{
|
||||
u16 ws_row;
|
||||
u16 ws_col;
|
||||
u16 ws_xpixel;
|
||||
u16 ws_ypixel;
|
||||
__u16_t ws_row;
|
||||
__u16_t ws_col;
|
||||
__u16_t ws_xpixel;
|
||||
__u16_t ws_ypixel;
|
||||
};
|
||||
|
||||
// Values for c_lflag.
|
||||
#define ECHO 1
|
||||
#define TOSTOP 2
|
||||
#define ECHOCTL 4
|
||||
#define ISIG 8
|
||||
#define ICANON 16
|
||||
#define ECHOE 32
|
||||
#define NOFLSH 64
|
||||
|
||||
// termios ioctl() requests.
|
||||
#define TCGETS 0
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
#define _POSIX_CHOWN_RESTRICTED 200112L
|
||||
#define _POSIX_SHELL 200112L
|
||||
#define _POSIX_VDISABLE (-2)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
|
Loading…
Reference in New Issue
Block a user