From fa9c7007b4b45727b217f7a7757ceaded38d50b8 Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Thu, 9 Feb 2012 23:55:03 +0100 Subject: [PATCH] Implemented a terminal that reads from keyboard and writes to kernel log. This terminal will provide the keyboard data in a couple modes as /dev/tty. --- sortix/Makefile | 1 + sortix/logterminal.cpp | 267 +++++++++++++++++++++++++++++++++++++++++ sortix/logterminal.h | 77 ++++++++++++ 3 files changed, 345 insertions(+) create mode 100644 sortix/logterminal.cpp create mode 100644 sortix/logterminal.h diff --git a/sortix/Makefile b/sortix/Makefile index 1b0f0182..e440eeb5 100644 --- a/sortix/Makefile +++ b/sortix/Makefile @@ -79,6 +79,7 @@ pci.o \ uart.o \ terminal.o \ linebuffer.o \ +logterminal.o \ vgaterminal.o \ serialterminal.o \ descriptors.o \ diff --git a/sortix/logterminal.cpp b/sortix/logterminal.cpp new file mode 100644 index 00000000..c8285b44 --- /dev/null +++ b/sortix/logterminal.cpp @@ -0,0 +1,267 @@ +/****************************************************************************** + + COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2012. + + This file is part of Sortix. + + Sortix 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. + + Sortix 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 Sortix. If not, see . + + logterminal.cpp + A simple terminal that writes to the kernel log. + +******************************************************************************/ + +#include "platform.h" +#include +#include +#include "utf8.h" +#include "keyboard.h" +#include "scheduler.h" +#include "keycodes.h" +#include "terminal.h" +#include "logterminal.h" + +using namespace Maxsi; + +namespace Sortix +{ + const unsigned SUPPORTED_MODES = TERMMODE_KBKEY + | TERMMODE_UNICODE + | TERMMODE_SIGNAL + | TERMMODE_UTF8 + | TERMMODE_LINEBUFFER + | TERMMODE_ECHO + | TERMMODE_NONBLOCK; + + LogTerminal::LogTerminal(Keyboard* keyboard, KeyboardLayout* kblayout) + { + this->keyboard = keyboard; + this->kblayout = kblayout; + this->partiallywritten = 0; + this->control = false; + this->mode = TERMMODE_UNICODE + | TERMMODE_SIGNAL + | TERMMODE_UTF8 + | TERMMODE_LINEBUFFER + | TERMMODE_ECHO; + keyboard->SetOwner(this, NULL); + } + + LogTerminal::~LogTerminal() + { + delete keyboard; + delete kblayout; + } + + bool LogTerminal::SetMode(unsigned newmode) + { + unsigned oldmode = mode; + if ( oldmode & ~SUPPORTED_MODES ) { Error::Set(ENOSYS); return false; } + bool oldutf8 = mode & TERMMODE_UTF8; + bool newutf8 = newmode & TERMMODE_UTF8; + if ( oldutf8 ^ newutf8 ) { partiallywritten = 0; } + mode = newmode; + if ( !(newmode & TERMMODE_LINEBUFFER) ) { CommitLineBuffer(); } + partiallywritten = 0; + return true; + } + + bool LogTerminal::SetWidth(unsigned width) + { + if ( width != GetWidth() ) { Error::Set(ENOTSUP); return false; } + return true; + } + + bool LogTerminal::SetHeight(unsigned height) + { + if ( height != GetHeight() ) { Error::Set(ENOTSUP); return false; } + return true; + } + + unsigned LogTerminal::GetMode() const + { + return mode; + } + + unsigned LogTerminal::GetWidth() const + { + return 80U; + } + + unsigned LogTerminal::GetHeight() const + { + return 25U; + } + + void LogTerminal::OnKeystroke(Keyboard* kb, void* /*user*/) + { + while ( kb->HasPending() ) + { + ProcessKeystroke(kb->Read()); + } + } + + void LogTerminal::ProcessKeystroke(int kbkey) + { + if ( !kbkey ) { return; } + + if ( kbkey == KBKEY_LCTRL ) { control = true; } + if ( kbkey == -KBKEY_LCTRL ) { control = false; } + if ( mode & TERMMODE_SIGNAL && control && kbkey == KBKEY_C ) + { + Scheduler::SigIntHack(); + return; + } + + uint32_t unikbkey = KBKEY_ENCODE(kbkey); + QueueUnicode(unikbkey); + uint32_t unicode = kblayout->Translate(kbkey); + if ( 0 < kbkey ) { QueueUnicode(unicode); } + } + + void LogTerminal::QueueUnicode(uint32_t unicode) + { + if ( !unicode ) { return; } + + int kbkey = KBKEY_DECODE(unicode); + int abskbkey = (kbkey < 0) ? -kbkey : kbkey; + bool wasenter = kbkey == KBKEY_ENTER || unicode == '\n'; + bool kbkeymode = mode & TERMMODE_KBKEY; + bool unicodemode = mode && TERMMODE_UNICODE; + bool linemode = mode & TERMMODE_LINEBUFFER; + bool echomode = mode & TERMMODE_ECHO; + + if ( linemode && abskbkey == KBKEY_BKSPC ) { return; } + while ( linemode && unicode == '\b' ) + { + if ( !linebuffer.CanBackspace() ) { return; } + uint32_t delchar = linebuffer.Backspace(); + bool waskbkey = KBKEY_DECODE(delchar); + bool wasuni = !waskbkey; + if ( waskbkey && !kbkeymode ) { continue; } + if ( wasuni && !unicodemode ) { continue; } + if ( !echomode ) { return; } + if ( wasuni ) { Log::Print("\b"); } + return; + } + + if ( !linebuffer.Push(unicode) ) + { + Log::PrintF("Warning: LogTerminal driver dropping keystroke due " + "to insufficient buffer space\n"); + return; + } + + // TODO: Could it be the right thing to print the unicode character even + // if it is unprintable (it's an encoded keystroke)? + if ( !KBKEY_DECODE(unicode) && echomode ) + { + char utf8buf[6]; + unsigned numbytes = UTF8::Encode(unicode, utf8buf); + Log::PrintData(utf8buf, numbytes); + } + + bool commit = !linemode || wasenter; + if ( commit ) { CommitLineBuffer(); } + } + + void LogTerminal::CommitLineBuffer() + { + linebuffer.Commit(); + queuecommitevent.Signal(); + } + + ssize_t LogTerminal::Read(byte* dest, size_t count) + { + size_t sofar = 0; + size_t left = count; + while ( left && linebuffer.CanPop() ) + { + uint32_t codepoint = linebuffer.Peek(); + int kbkey = KBKEY_DECODE(codepoint); + + bool ignore = false; + if ( !(mode & TERMMODE_KBKEY) && kbkey ) { ignore = true; } + if ( !(mode & TERMMODE_UNICODE) && !kbkey ) { ignore = true; } + if ( ignore ) { linebuffer.Pop(); continue; } + + byte* buf; + size_t bufsize; + uint8_t codepointbuf[4]; + char utf8buf[6]; + + if ( mode & TERMMODE_UTF8 ) + { + unsigned numbytes = UTF8::Encode(codepoint, utf8buf); + if ( !numbytes ) + { + Log::PrintF("Warning: logterminal driver dropping invalid " + "codepoint 0x%x\n", codepoint); + } + buf = (byte*) utf8buf; + bufsize = numbytes; + } + else + { + codepointbuf[0] = (codepoint >> 24U) & 0xFFU; + codepointbuf[1] = (codepoint >> 16U) & 0xFFU; + codepointbuf[2] = (codepoint >> 8U) & 0xFFU; + codepointbuf[3] = (codepoint >> 0U) & 0xFFU; + buf = (byte*) &codepointbuf; + bufsize = sizeof(codepointbuf); + // TODO: Whoops, the above is big-endian and the user programs + // expect the data to be in the host endian. That's bad. For now + // just send the data in host endian, but this will break when + // terminals are accessed over the network. + buf = (byte*) &codepoint; + } + if ( bufsize < partiallywritten ) { partiallywritten = bufsize; } + buf += partiallywritten; + bufsize -= partiallywritten; + if ( sofar && left < bufsize ) { return sofar; } + size_t amount = left < bufsize ? left : bufsize; + Memory::Copy(dest + sofar, buf, amount); + partiallywritten = (amount < bufsize) ? partiallywritten + amount : 0; + left -= amount; + sofar += amount; + if ( !partiallywritten ) { linebuffer.Pop(); } + } + + // Block if no data were ready. + if ( !sofar ) + { + if ( mode & TERMMODE_NONBLOCK ) { Error::Set(EWOULDBLOCK); } + else { queuecommitevent.Register(); Error::Set(EBLOCKING); } + return -1; + } + + return sofar; + } + + ssize_t LogTerminal::Write(const byte* src, size_t count) + { + Log::PrintData(src, count); + return count; + } + + bool LogTerminal::IsReadable() + { + return true; + } + + bool LogTerminal::IsWritable() + { + return true; + } +} diff --git a/sortix/logterminal.h b/sortix/logterminal.h new file mode 100644 index 00000000..c2076e30 --- /dev/null +++ b/sortix/logterminal.h @@ -0,0 +1,77 @@ +/****************************************************************************** + + COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2012. + + This file is part of Sortix. + + Sortix 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. + + Sortix 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 Sortix. If not, see . + + logterminal.h + A simple terminal that writes to the kernel log. + +******************************************************************************/ + +#ifndef SORTIX_LOGTERMINAL_H +#define SORTIX_LOGTERMINAL_H + +#include "event.h" +#include "stream.h" +#include "terminal.h" +#include "keyboard.h" +#include "linebuffer.h" + +namespace Sortix +{ + class LogTerminal : public DevTerminal, public KeyboardOwner + { + public: + LogTerminal(Keyboard* keyboard, KeyboardLayout* kblayout); + virtual ~LogTerminal(); + + public: + virtual ssize_t Read(byte* dest, size_t count); + virtual ssize_t Write(const byte* src, size_t count); + virtual bool IsReadable(); + virtual bool IsWritable(); + + public: + virtual bool SetMode(unsigned mode); + virtual bool SetWidth(unsigned width); + virtual bool SetHeight(unsigned height); + virtual unsigned GetMode() const; + virtual unsigned GetWidth() const; + virtual unsigned GetHeight() const; + + public: + virtual void OnKeystroke(Keyboard* keyboard, void* user); + + private: + void ProcessKeystroke(int kbkey); + void QueueUnicode(uint32_t unicode); + void CommitLineBuffer(); + + private: + Keyboard* keyboard; + KeyboardLayout* kblayout; + LineBuffer linebuffer; + Event queuecommitevent; + size_t partiallywritten; + unsigned mode; + bool control; + + }; +} + +#endif +