feat(kernel/tty): implement processing control characters (^C, ^D, etc)

This commit is contained in:
2025-04-13 09:49:09 +00:00
parent 5293bb2039
commit 11d326b693
7 changed files with 377 additions and 250 deletions

View File

@ -24,16 +24,6 @@
namespace TTY
{
PTMXDevice::PTMXDevice()
{
}
PTMXDevice::~PTMXDevice()
{
for (auto pty : PTYs)
delete pty;
}
int PTMXDevice::Open()
{
stub;

View File

@ -1,95 +0,0 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <tty.hpp>
#include <filesystem/ioctl.hpp>
#include <string.h>
#include <errno.h>
#include "../kernel.h"
namespace TTY
{
PTYDevice::PTYDevice()
: Master(), Slave()
{
}
PTYDevice::~PTYDevice()
{
}
int PTYDevice::Open()
{
stub;
return -ENOSYS;
}
int PTYDevice::Close()
{
stub;
return -ENOSYS;
}
ssize_t PTYDevice::Read(void *Buffer, size_t Size)
{
return Slave.Read(Buffer, Size);
}
ssize_t PTYDevice::Write(const void *Buffer, size_t Size)
{
return Master.Write(Buffer, Size);
}
PTYDevice::PTYMaster::PTYMaster()
: TermBuf(1024)
{
}
PTYDevice::PTYMaster::~PTYMaster()
{
}
ssize_t PTYDevice::PTYMaster::Read(void *Buffer, size_t Size)
{
return TermBuf.Read((char *)Buffer, Size);
}
ssize_t PTYDevice::PTYMaster::Write(const void *Buffer, size_t Size)
{
return TermBuf.Write((const char *)Buffer, Size);
}
PTYDevice::PTYSlave::PTYSlave()
: TermBuf(1024)
{
}
PTYDevice::PTYSlave::~PTYSlave()
{
}
ssize_t PTYDevice::PTYSlave::Read(void *Buffer, size_t Size)
{
return TermBuf.Read((char *)Buffer, Size);
}
ssize_t PTYDevice::PTYSlave::Write(const void *Buffer, size_t Size)
{
return TermBuf.Write((const char *)Buffer, Size);
}
}

View File

@ -26,76 +26,12 @@
namespace TTY
{
ssize_t TerminalBuffer::Read(char *OutputBuffer, size_t Size)
TeletypeDriver::TeletypeDriver() : TermBuf(1024)
{
std::lock_guard<std::mutex> lock(Mutex);
size_t bytesRead = 0;
while (bytesRead < Size && ReadIndex != WriteIndex)
{
OutputBuffer[bytesRead++] = Buffer[ReadIndex];
ReadIndex = (ReadIndex + 1) % Buffer.size();
}
return bytesRead;
}
ssize_t TerminalBuffer::Write(const char *InputBuffer, size_t Size)
{
std::lock_guard<std::mutex> lock(Mutex);
size_t bytesWritten = 0;
for (size_t i = 0; i < Size; ++i)
{
Buffer[WriteIndex] = InputBuffer[i];
WriteIndex = (WriteIndex + 1) % Buffer.size();
bytesWritten++;
}
return bytesWritten;
}
/* ======================================================================== */
int TeletypeDriver::Open(int Flags, mode_t Mode)
{
warn("Unimplemented open(%#x, %#x)", Flags, Mode);
return -ENOSYS;
}
int TeletypeDriver::Close()
{
warn("Unimplemented close()");
return -ENOSYS;
}
ssize_t TeletypeDriver::Read(void *Buffer, size_t Size, off_t Offset)
{
warn("Unimplemented read(%#lx, %#lx, %#lx)", Buffer, Size, Offset);
return -ENOSYS;
}
ssize_t TeletypeDriver::Write(const void *Buffer, size_t Size, off_t Offset)
{
warn("Unimplemented write(%#lx, %#lx, %#lx)", Buffer, Size, Offset);
return -ENOSYS;
}
int TeletypeDriver::Ioctl(unsigned long Request, void *Argp)
{
warn("Unimplemented ioctl(%#lx, %#lx)", Request, Argp);
return -ENOSYS;
}
TeletypeDriver::TeletypeDriver()
: TermBuf(1024)
{
this->TerminalSize = {
.ws_row = 0,
.ws_col = 0,
.ws_xpixel = 0,
.ws_ypixel = 0,
};
if (thisProcess)
this->ProcessGroup = thisProcess->Security.ProcessGroupID;
else
this->ProcessGroup = 0;
/*
- ICRNL - Map Carriage Return to New Line
@ -110,32 +46,24 @@ namespace TTY
- ECHO - Echo input characters
- ICANON - Enable canonical input (enable line editing)
- ISIG - Enable signals
*/
this->TerminalConfig.c_iflag = /*ICRNL |*/ IXON;
this->TerminalConfig.c_oflag = OPOST | ONLCR;
this->TerminalConfig.c_cflag = CS8 | CREAD | HUPCL;
this->TerminalConfig.c_lflag = ECHO | ICANON;
this->TerminalConfig.c_lflag = ECHO | ICANON | ISIG;
this->TerminalConfig.c_cc[VINTR] = 0x03; /* ^C */
this->TerminalConfig.c_cc[VQUIT] = 0x1C; /* ^\ */
this->TerminalConfig.c_cc[VERASE] = 0x7F; /* DEL */
this->TerminalConfig.c_cc[VKILL] = 0x15; /* ^U */
this->TerminalConfig.c_cc[VEOF] = 0x04; /* ^D */
this->TerminalConfig.c_cc[VTIME] = 0; /* Timeout for non-canonical read */
this->TerminalConfig.c_cc[VMIN] = 1; /* Minimum number of characters for non-canonical read */
this->TerminalConfig.c_cc[VSWTC] = 0; /* ^O */
this->TerminalConfig.c_cc[VSTART] = 0x11; /* ^Q */
this->TerminalConfig.c_cc[VSTOP] = 0x13; /* ^S */
this->TerminalConfig.c_cc[VSUSP] = 0x1A; /* ^Z */
this->TerminalConfig.c_cc[VEOL] = 0x00; /* NUL */
this->TerminalConfig.c_cc[VREPRINT] = 0x12; /* ^R */
this->TerminalConfig.c_cc[VDISCARD] = 0x14; /* ^T */
this->TerminalConfig.c_cc[VWERASE] = 0x17; /* ^W */
this->TerminalConfig.c_cc[VLNEXT] = 0x19; /* ^Y */
this->TerminalConfig.c_cc[VEOL2] = 0x7F; /* DEL (or sometimes EOF) */
}
TeletypeDriver::~TeletypeDriver()
{
this->TerminalConfig.c_cc[VINTR] = 'C' - 0x40;
this->TerminalConfig.c_cc[VQUIT] = '\\' - 0x40;
this->TerminalConfig.c_cc[VERASE] = '\177';
this->TerminalConfig.c_cc[VKILL] = 'U' - 0x40;
this->TerminalConfig.c_cc[VEOF] = 'D' - 0x40;
this->TerminalConfig.c_cc[VSTART] = 'Q' - 0x40;
this->TerminalConfig.c_cc[VSTOP] = 'S' - 0x40;
this->TerminalConfig.c_cc[VSUSP] = 'Z' - 0x40;
this->TerminalConfig.c_cc[VREPRINT] = 'R' - 0x40;
this->TerminalConfig.c_cc[VDISCARD] = 'O' - 0x40;
this->TerminalConfig.c_cc[VWERASE] = 'W' - 0x40;
this->TerminalConfig.c_cc[VLNEXT] = 'V' - 0x40;
}
}

View File

@ -29,26 +29,27 @@ namespace KernelConsole
{
int VirtualTerminal::Open(int Flags, mode_t Mode)
{
std::lock_guard<std::mutex> lock(Mutex);
std::lock_guard<std::mutex> lock(vt_mutex);
stub;
return 0;
}
int VirtualTerminal::Close()
{
std::lock_guard<std::mutex> lock(Mutex);
std::lock_guard<std::mutex> lock(vt_mutex);
stub;
return 0;
}
ssize_t VirtualTerminal::Read(void *Buffer, size_t Size, off_t Offset)
{
std::lock_guard<std::mutex> lock(Mutex);
std::lock_guard<std::mutex> lock(vt_mutex);
KeyboardReport report{};
/* FIXME: this is a hack, "static" is not a good idea */
static bool upperCase = false;
static bool controlKey = false;
RecheckKeyboard:
while (DriverManager->GlobalKeyboardInputReports.Count() == 0)
@ -65,6 +66,36 @@ namespace KernelConsole
upperCase = false;
goto RecheckKeyboard;
}
else if (pkey == KEY_LEFT_CTRL || pkey == KEY_RIGHT_CTRL)
{
if (report.Key & KEY_PRESSED)
controlKey = true;
else
controlKey = false;
debug("controlKey = %d", controlKey);
goto RecheckKeyboard;
}
if (controlKey && this->TerminalConfig.c_lflag & ICANON)
{
if (report.Key & KEY_PRESSED)
{
char cc = Driver::GetControlCharacter(report.Key);
if (cc == 0x00)
goto RecheckKeyboard;
if (this->TerminalConfig.c_lflag & ECHO)
{
char c = Driver::GetScanCode(report.Key, true);
this->Append('^');
this->Append(c);
this->Append('\n');
}
this->Process(cc);
goto RecheckKeyboard;
}
}
if (!(report.Key & KEY_PRESSED))
goto RecheckKeyboard;
@ -99,7 +130,7 @@ namespace KernelConsole
ssize_t VirtualTerminal::Write(const void *Buffer, size_t Size, off_t Offset)
{
std::lock_guard<std::mutex> lock(Mutex);
std::lock_guard<std::mutex> lock(vt_mutex);
char *buf = (char *)Buffer;
debug("string: \"%*s\"", Size, buf);
@ -117,7 +148,7 @@ namespace KernelConsole
int VirtualTerminal::Ioctl(unsigned long Request, void *Argp)
{
std::lock_guard<std::mutex> lock(Mutex);
std::lock_guard<std::mutex> lock(vt_mutex);
switch (Request)
{
@ -166,14 +197,14 @@ namespace KernelConsole
}
case TIOCGPGRP:
{
*((pid_t *)Argp) = thisProcess->Security.ProcessGroupID;
debug("returning pgid %d", thisProcess->Security.ProcessGroupID);
*((pid_t *)Argp) = this->ProcessGroup;
debug("returning pgid %d", this->ProcessGroup);
return 0;
}
case TIOCSPGRP:
{
thisProcess->Security.ProcessGroupID = *((pid_t *)Argp);
debug("updated pgid to %d", thisProcess->Security.ProcessGroupID);
this->ProcessGroup = *((pid_t *)Argp);
debug("updated pgid to %d", this->ProcessGroup);
return 0;
}
case TIOCGSID:
@ -461,6 +492,133 @@ namespace KernelConsole
CursorCB(&Cursor);
}
void VirtualTerminal::ProcessControlCharacter(char c)
{
auto ccheck = [&](int v)
{
return (this->TerminalConfig.c_cc[v] != 0x00 &&
this->TerminalConfig.c_cc[v] == c);
};
auto ciflag = [&](int f)
{
return (this->TerminalConfig.c_iflag & f) != 0;
};
auto clflag = [&](int f)
{
return (this->TerminalConfig.c_lflag & f) != 0;
};
if (ciflag(IXON) && ccheck(VSTOP))
{
fixme("flow control: stopping output");
return;
}
if (ciflag(IXON) && ccheck(VSTART))
{
fixme("flow control: resuming output");
return;
}
if (clflag(ISIG))
{
if (ccheck(VINTR))
{
if (this->ProcessGroup == 0)
{
debug("Process group is 0!!!");
return;
}
for (auto proc : thisProcess->GetContext()->GetProcessList())
{
if (proc->Security.ProcessGroupID != this->ProcessGroup)
continue;
debug("Sending signal SIGINT to %s(%d)", proc->Name, proc->ID);
proc->SendSignal(SIGINT);
}
return;
}
else if (ccheck(VQUIT))
{
if (this->ProcessGroup == 0)
{
debug("Process group is 0!!!");
return;
}
for (auto proc : thisProcess->GetContext()->GetProcessList())
{
if (proc->Security.ProcessGroupID != this->ProcessGroup)
continue;
debug("Sending signal SIGQUIT to %s(%d)", proc->Name, proc->ID);
proc->SendSignal(SIGQUIT);
}
return;
}
else if (ccheck(VSUSP))
{
if (this->ProcessGroup == 0)
{
debug("Process group is 0!!!");
return;
}
for (auto proc : thisProcess->GetContext()->GetProcessList())
{
if (proc->Security.ProcessGroupID != this->ProcessGroup)
continue;
debug("Sending signal SIGTSTP to %s(%d)", proc->Name, proc->ID);
proc->SendSignal(SIGTSTP);
}
return;
}
}
if (c == '\r')
{
if (ciflag(IGNCR))
return;
if (ciflag(ICRNL))
c = '\n';
}
else if (c == '\n' && (ciflag(INLCR)))
c = '\r';
if (clflag(ICANON))
{
if (ccheck(VERASE))
{
if (this->Cursor.X > 0)
{
this->Cursor.X--;
this->Append('\b');
this->Append(' ');
this->Append('\b');
}
return;
}
else if (ccheck(VKILL))
{
fixme("clear the current line");
return;
}
}
if (clflag(ECHO))
{
if (c == '\n')
this->Append('\n');
else
this->Append(c);
}
}
void VirtualTerminal::Process(char c)
{
#ifdef DEBUG
@ -496,6 +654,15 @@ namespace KernelConsole
#endif
#endif
if (this->TerminalConfig.c_lflag & ICANON)
{
if ((c > 0x00 && c <= 0x1F) && c != '\x1b')
{
this->ProcessControlCharacter(c);
return;
}
}
ANSIParser *parser = &this->Parser;
switch (parser->State)
@ -636,30 +803,28 @@ namespace KernelConsole
- ECHO - Echo input characters
- ICANON - Enable canonical input (enable line editing)
- ISIG - Enable signals
*/
this->TerminalConfig.c_iflag = /*ICRNL |*/ IXON;
this->TerminalConfig.c_oflag = OPOST | ONLCR;
this->TerminalConfig.c_cflag = CS8 | CREAD | HUPCL;
this->TerminalConfig.c_lflag = ECHO | ICANON;
this->TerminalConfig.c_lflag &= ~ICANON; /* FIXME: not ready for this yet */
this->TerminalConfig.c_lflag = ECHO | ICANON | ISIG;
this->TerminalConfig.c_cc[VINTR] = 0x03; /* ^C */
this->TerminalConfig.c_cc[VQUIT] = 0x1C; /* ^\ */
this->TerminalConfig.c_cc[VERASE] = 0x7F; /* DEL */
this->TerminalConfig.c_cc[VKILL] = 0x15; /* ^U */
this->TerminalConfig.c_cc[VEOF] = 0x04; /* ^D */
this->TerminalConfig.c_cc[VTIME] = 0; /* Timeout for non-canonical read */
this->TerminalConfig.c_cc[VMIN] = 1; /* Minimum number of characters for non-canonical read */
this->TerminalConfig.c_cc[VSWTC] = 0; /* ^O */
this->TerminalConfig.c_cc[VSTART] = 0x11; /* ^Q */
this->TerminalConfig.c_cc[VSTOP] = 0x13; /* ^S */
this->TerminalConfig.c_cc[VSUSP] = 0x1A; /* ^Z */
this->TerminalConfig.c_cc[VEOL] = 0x00; /* NUL */
this->TerminalConfig.c_cc[VREPRINT] = 0x12; /* ^R */
this->TerminalConfig.c_cc[VDISCARD] = 0x14; /* ^T */
this->TerminalConfig.c_cc[VWERASE] = 0x17; /* ^W */
this->TerminalConfig.c_cc[VLNEXT] = 0x19; /* ^Y */
this->TerminalConfig.c_cc[VEOL2] = 0x7F; /* DEL (or sometimes EOF) */
this->TerminalConfig.c_cc[VINTR] = 'C' - 0x40;
this->TerminalConfig.c_cc[VQUIT] = '\\' - 0x40;
this->TerminalConfig.c_cc[VERASE] = '\177';
this->TerminalConfig.c_cc[VKILL] = 'U' - 0x40;
this->TerminalConfig.c_cc[VEOF] = 'D' - 0x40;
this->TerminalConfig.c_cc[VSTART] = 'Q' - 0x40;
this->TerminalConfig.c_cc[VSTOP] = 'S' - 0x40;
this->TerminalConfig.c_cc[VSUSP] = 'Z' - 0x40;
this->TerminalConfig.c_cc[VREPRINT] = 'R' - 0x40;
this->TerminalConfig.c_cc[VDISCARD] = 'O' - 0x40;
this->TerminalConfig.c_cc[VWERASE] = 'W' - 0x40;
this->TerminalConfig.c_cc[VLNEXT] = 'V' - 0x40;
this->TerminalConfig.c_cc[VTIME] = 0; /* Timeout for non-canonical read */
this->TerminalConfig.c_cc[VMIN] = 1; /* Minimum number of characters for non-canonical read */
this->Cells = new TerminalCell[Rows * Columns];
@ -667,8 +832,5 @@ namespace KernelConsole
(Rows * Columns) * sizeof(TerminalCell));
}
VirtualTerminal::~VirtualTerminal()
{
delete[] this->Cells;
}
VirtualTerminal::~VirtualTerminal() { delete[] this->Cells; }
}