From 160bd2ffca5cc2603fe4f32697dc09aa44a1e5fc Mon Sep 17 00:00:00 2001 From: Michael Lotz Date: Sun, 27 Mar 2005 06:11:38 +0000 Subject: [PATCH] This is a minimalistic version of a terminal. It can be run under R5 and Haiku (binary compatible) and should work with the Haiku app_server and input_server. It gives access to a /bin/sh like a normal terminal, but has no fancy features like different text encoding support and it cannot even redraw itself when something else overdraws it. But it's pretty impressiv that it works so well under Haiku. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@12068 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/tests/apps/miniterminal/Console.cpp | 576 +++++++++++++++++++++ src/tests/apps/miniterminal/Console.h | 96 ++++ src/tests/apps/miniterminal/Jamfile | 13 + src/tests/apps/miniterminal/MiniApp.cpp | 39 ++ src/tests/apps/miniterminal/MiniApp.h | 19 + src/tests/apps/miniterminal/MiniView.cpp | 306 +++++++++++ src/tests/apps/miniterminal/MiniView.h | 24 + src/tests/apps/miniterminal/MiniWin.cpp | 29 ++ src/tests/apps/miniterminal/MiniWin.h | 19 + src/tests/apps/miniterminal/ViewBuffer.cpp | 232 +++++++++ src/tests/apps/miniterminal/ViewBuffer.h | 43 ++ 11 files changed, 1396 insertions(+) create mode 100644 src/tests/apps/miniterminal/Console.cpp create mode 100644 src/tests/apps/miniterminal/Console.h create mode 100644 src/tests/apps/miniterminal/Jamfile create mode 100644 src/tests/apps/miniterminal/MiniApp.cpp create mode 100644 src/tests/apps/miniterminal/MiniApp.h create mode 100644 src/tests/apps/miniterminal/MiniView.cpp create mode 100644 src/tests/apps/miniterminal/MiniView.h create mode 100644 src/tests/apps/miniterminal/MiniWin.cpp create mode 100644 src/tests/apps/miniterminal/MiniWin.h create mode 100644 src/tests/apps/miniterminal/ViewBuffer.cpp create mode 100644 src/tests/apps/miniterminal/ViewBuffer.h diff --git a/src/tests/apps/miniterminal/Console.cpp b/src/tests/apps/miniterminal/Console.cpp new file mode 100644 index 0000000000..0336b47886 --- /dev/null +++ b/src/tests/apps/miniterminal/Console.cpp @@ -0,0 +1,576 @@ +/* + * Console.cpp - Mimicing the console driver + * Based on the console driver. + * + * Copyright 2005 Michael Lotz. All rights reserved. + * Distributed under the Haiku License. + * + * Copyright 2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved. + * Distributed under the terms of the NewOS License. + */ + +#include "Console.h" +#include "ViewBuffer.h" +#include + +Console::Console(ViewBuffer *output) + : fOutput(output) +{ + fOutput->GetSize(&fColumns, &fLines); + fOutput->SetResizeCallback(&ResizeCallback, this); + ResetConsole(); + GotoXY(0, 0); + SaveCursor(true); + fOutput->Clear(0x0f); +} + + +Console::~Console() +{ +} + + +void +Console::ResetConsole() +{ + fAttr = 0x0f; + fScrollTop = 0; + fScrollBottom = fLines - 1; + fBrightAttr = true; + fReverseAttr = false; +} + + +void +Console::ResizeCallback(int32 width, int32 height, void *data) +{ + Console *console = (Console *)data; + + console->fColumns = width; + console->fLines = height; + console->SetScrollRegion(console->fScrollTop, height - 1); +} + + +void +Console::SetScrollRegion(int top, int bottom) +{ + if (top < 0) + top = 0; + if (bottom >= fLines) + bottom = fLines - 1; + if (top > bottom) + return; + + fScrollTop = top; + fScrollBottom = bottom; +} + + +void +Console::ScrollUp() +{ + // see if cursor is outside of scroll region + if (fY < fScrollTop || fY > fScrollBottom) + return; + + if (fY - fScrollTop > 1) { + // move the screen up one + fOutput->Blit(0, fScrollTop + 1, fColumns, fY - fScrollTop, 0, fScrollTop); + } + + // clear the bottom line + fOutput->FillGlyph(0, fY, fColumns, 1, ' ', fAttr); +} + + +void +Console::ScrollDown() +{ + // see if cursor is outside of scroll region + if (fY < fScrollTop || fY > fScrollBottom) + return; + + if (fScrollBottom - fY > 1) { + // move the screen down one + fOutput->Blit(0, fY, fColumns, fScrollBottom - fY, 0, fY + 1); + } + + // clear the top line + fOutput->FillGlyph(0, fY, fColumns, 1, ' ', fAttr); +} + + +void +Console::LineFeed() +{ + if (fY == fScrollBottom) { + // we hit the bottom of our scroll region + ScrollUp(); + } else if (fY < fScrollBottom) { + fY++; + } +} + + +void +Console::RLineFeed() +{ + if (fY == fScrollTop) { + // we hit the top of our scroll region + ScrollDown(); + } else if (fY > fScrollTop) { + fY--; + } +} + + +void +Console::CariageReturn() +{ + fX = 0; +} + + +void +Console::Delete() +{ + if (fX > 0) { + fX--; + } else if (fY > 0) { + fY--; + fX = fColumns - 1; + } else { + ScrollDown(); + fY--; + fX = fColumns - 1; + return; + } + + fOutput->PutGlyph(fX, fY, ' ', fAttr); +} + + +void +Console::Tab() +{ + fX = (fX + TAB_SIZE) & ~TAB_MASK; + if (fX >= fColumns) { + fX -= fColumns; + LineFeed(); + } +} + + +void +Console::EraseLine(erase_line_mode mode) +{ + switch (mode) { + case LINE_ERASE_WHOLE: + fOutput->FillGlyph(0, fY, fColumns, 1, ' ', fAttr); + break; + case LINE_ERASE_LEFT: + fOutput->FillGlyph(0, fY, fX + 1, 1, ' ', fAttr); + break; + case LINE_ERASE_RIGHT: + fOutput->FillGlyph(fX, fY, fColumns - fX, 1, ' ', fAttr); + break; + default: + return; + } +} + + +void +Console::EraseScreen(erase_screen_mode mode) +{ + switch (mode) { + case SCREEN_ERASE_WHOLE: + fOutput->Clear(fAttr); + break; + case SCREEN_ERASE_UP: + fOutput->FillGlyph(0, 0, fColumns, fY + 1, ' ', fAttr); + break; + case SCREEN_ERASE_DOWN: + fOutput->FillGlyph(fY, 0, fColumns, fLines - fY, ' ', fAttr); + break; + default: + return; + } +} + + +void +Console::SaveCursor(bool save_attrs) +{ + fSavedX = fX; + fSavedY = fY; + + if (save_attrs) + fSavedAttr = fAttr; +} + + +void +Console::RestoreCursor(bool restore_attrs) +{ + fX = fSavedX; + fY = fSavedY; + + if (restore_attrs) + fAttr = fSavedAttr; +} + + +void +Console::UpdateCursor(int x, int y) +{ + fOutput->MoveCursor(x, y); +} + + +void +Console::GotoXY(int new_x, int new_y) +{ + if (new_x >= fColumns) + new_x = fColumns - 1; + if (new_x < 0) + new_x = 0; + if (new_y >= fLines) + new_y = fLines - 1; + if (new_y < 0) + new_y = 0; + + fX = new_x; + fY = new_y; +} + + +void +Console::PutChar(const char c) +{ + fOutput->PutGlyph(fX, fY, c, fAttr); + if (++fX >= fColumns) { + CariageReturn(); + LineFeed(); + } +} + + +void +Console::SetVT100Attributes(int32 *args, int32 argCount) +{ + if (argCount == 0) { + // that's the default (attributes off) + argCount++; + args[0] = 0; + } + + for (int32 i = 0; i < argCount; i++) { + switch (args[i]) { + case 0: // reset + fAttr = 0x0f; + fBrightAttr = true; + fReverseAttr = false; + break; + case 1: // bright + fBrightAttr = true; + fAttr |= 0x08; // set the bright bit + break; + case 2: // dim + fBrightAttr = false; + fAttr &= ~0x08; // unset the bright bit + break; + case 4: // underscore we can't do + break; + case 5: // blink + fAttr |= 0x80; // set the blink bit + break; + case 7: // reverse + fReverseAttr = true; + fAttr = ((fAttr & BMASK) >> 4) | ((fAttr & FMASK) << 4); + if (fBrightAttr) + fAttr |= 0x08; + break; + case 8: // hidden? + break; + + /* foreground colors */ + case 30: fAttr = (fAttr & ~FMASK) | 0 | (fBrightAttr ? 0x08 : 0); break; // black + case 31: fAttr = (fAttr & ~FMASK) | 4 | (fBrightAttr ? 0x08 : 0); break; // red + case 32: fAttr = (fAttr & ~FMASK) | 2 | (fBrightAttr ? 0x08 : 0); break; // green + case 33: fAttr = (fAttr & ~FMASK) | 6 | (fBrightAttr ? 0x08 : 0); break; // yellow + case 34: fAttr = (fAttr & ~FMASK) | 1 | (fBrightAttr ? 0x08 : 0); break; // blue + case 35: fAttr = (fAttr & ~FMASK) | 5 | (fBrightAttr ? 0x08 : 0); break; // magenta + case 36: fAttr = (fAttr & ~FMASK) | 3 | (fBrightAttr ? 0x08 : 0); break; // cyan + case 37: fAttr = (fAttr & ~FMASK) | 7 | (fBrightAttr ? 0x08 : 0); break; // white + + /* background colors */ + case 40: fAttr = (fAttr & ~BMASK) | (0 << 4); break; // black + case 41: fAttr = (fAttr & ~BMASK) | (4 << 4); break; // red + case 42: fAttr = (fAttr & ~BMASK) | (2 << 4); break; // green + case 43: fAttr = (fAttr & ~BMASK) | (6 << 4); break; // yellow + case 44: fAttr = (fAttr & ~BMASK) | (1 << 4); break; // blue + case 45: fAttr = (fAttr & ~BMASK) | (5 << 4); break; // magenta + case 46: fAttr = (fAttr & ~BMASK) | (3 << 4); break; // cyan + case 47: fAttr = (fAttr & ~BMASK) | (7 << 4); break; // white + } + } +} + + +bool +Console::ProcessVT100Command(const char c, bool seen_bracket, int32 *args, int32 argCount) +{ + bool ret = true; + + if (seen_bracket) { + switch(c) { + case 'H': /* set cursor position */ + case 'f': { + int32 row = argCount > 0 ? args[0] : 1; + int32 col = argCount > 1 ? args[1] : 1; + if (row > 0) + row--; + if (col > 0) + col--; + GotoXY(col, row); + break; + } + case 'A': { /* move up */ + int32 deltay = argCount > 0 ? -args[0] : -1; + if (deltay == 0) + deltay = -1; + GotoXY(fX, fY + deltay); + break; + } + case 'e': + case 'B': { /* move down */ + int32 deltay = argCount > 0 ? args[0] : 1; + if (deltay == 0) + deltay = 1; + GotoXY(fX, fY + deltay); + break; + } + case 'D': { /* move left */ + int32 deltax = argCount > 0 ? -args[0] : -1; + if (deltax == 0) + deltax = -1; + GotoXY(fX + deltax, fY); + break; + } + case 'a': + case 'C': { /* move right */ + int32 deltax = argCount > 0 ? args[0] : 1; + if (deltax == 0) + deltax = 1; + GotoXY(fX + deltax, fY); + break; + } + case '`': + case 'G': { /* set X position */ + int32 newx = argCount > 0 ? args[0] : 1; + if (newx > 0) + newx--; + GotoXY(newx, fY); + break; + } + case 'd': { /* set y position */ + int32 newy = argCount > 0 ? args[0] : 1; + if (newy > 0) + newy--; + GotoXY(fX, newy); + break; + } + case 's': /* save current cursor */ + SaveCursor(false); + break; + case 'u': /* restore cursor */ + RestoreCursor(false); + break; + case 'r': { /* set scroll region */ + int32 low = argCount > 0 ? args[0] : 1; + int32 high = argCount > 1 ? args[1] : fLines; + if (low <= high) + SetScrollRegion(low - 1, high - 1); + break; + } + case 'L': { /* scroll virtual down at cursor */ + int32 lines = argCount > 0 ? args[0] : 1; + while (lines > 0) { + ScrollDown(); + lines--; + } + break; + } + case 'M': { /* scroll virtual up at cursor */ + int32 lines = argCount > 0 ? args[0] : 1; + while (lines > 0) { + ScrollUp(); + lines--; + } + break; + } + case 'K': + if (argCount == 0 || args[0] == 0) { + // erase to end of line + EraseLine(LINE_ERASE_RIGHT); + } else if (argCount > 0) { + if (args[0] == 1) + EraseLine(LINE_ERASE_LEFT); + else if (args[0] == 2) + EraseLine(LINE_ERASE_WHOLE); + } + break; + case 'J': + if (argCount == 0 || args[0] == 0) { + // erase to end of screen + EraseScreen(SCREEN_ERASE_DOWN); + } else if (argCount > 0) { + if (args[0] == 1) + EraseScreen(SCREEN_ERASE_UP); + else if (args[0] == 2) + EraseScreen(SCREEN_ERASE_WHOLE); + } + break; + case 'm': + if (argCount >= 0) + SetVT100Attributes(args, argCount); + break; + default: + ret = false; + } + } else { + switch (c) { + case 'c': + ResetConsole(); + break; + case 'D': + RLineFeed(); + break; + case 'M': + LineFeed(); + break; + case '7': + SaveCursor(true); + break; + case '8': + RestoreCursor(true); + break; + default: + ret = false; + } + } + + return ret; +} + + +void +Console::Write(const void *buf, size_t len) +{ + UpdateCursor(-1, -1); // hide the cursor + + const char *c; + size_t pos = 0; + + while (pos < len) { + c = &((const char *)buf)[pos++]; + + switch (fState) { + case CONSOLE_STATE_NORMAL: + // just output the stuff + switch (*c) { + case '\n': + LineFeed(); + break; + case '\r': + CariageReturn(); + break; + case 0x8: // backspace + Delete(); + break; + case '\t': + Tab(); + break; + case '\a': + // beep + printf("\n"); + break; + case '\0': + break; + case 0x1b: + // escape character + fArgCount = -1; + fState = CONSOLE_STATE_GOT_ESCAPE; + break; + default: + PutChar(*c); + } + break; + case CONSOLE_STATE_GOT_ESCAPE: + // look for either commands with no argument, or the '[' character + switch (*c) { + case '[': + fState = CONSOLE_STATE_SEEN_BRACKET; + break; + default: + fArgs[fArgCount] = 0; + ProcessVT100Command(*c, false, fArgs, fArgCount + 1); + fState = CONSOLE_STATE_NORMAL; + } + break; + case CONSOLE_STATE_SEEN_BRACKET: + switch (*c) { + case '0'...'9': + fArgCount = 0; + fArgs[fArgCount] = *c - '0'; + fState = CONSOLE_STATE_PARSING_ARG; + break; + case '?': + // private DEC mode parameter follows - we ignore those anyway + // ToDo: check if it was really used in combination with a mode command + break; + default: + ProcessVT100Command(*c, true, fArgs, fArgCount + 1); + fState = CONSOLE_STATE_NORMAL; + } + break; + case CONSOLE_STATE_NEW_ARG: + switch (*c) { + case '0'...'9': + fArgCount++; + if (fArgCount == MAX_ARGS) { + fState = CONSOLE_STATE_NORMAL; + break; + } + fArgs[fArgCount] = *c - '0'; + fState = CONSOLE_STATE_PARSING_ARG; + break; + default: + ProcessVT100Command(*c, true, fArgs, fArgCount + 1); + fState = CONSOLE_STATE_NORMAL; + } + break; + case CONSOLE_STATE_PARSING_ARG: + // parse args + switch (*c) { + case '0'...'9': + fArgs[fArgCount] *= 10; + fArgs[fArgCount] += *c - '0'; + break; + case ';': + fState = CONSOLE_STATE_NEW_ARG; + break; + default: + ProcessVT100Command(*c, true, fArgs, fArgCount + 1); + fState = CONSOLE_STATE_NORMAL; + } + } + } + + UpdateCursor(fX, fY); // show it again +} diff --git a/src/tests/apps/miniterminal/Console.h b/src/tests/apps/miniterminal/Console.h new file mode 100644 index 0000000000..e12357c5d6 --- /dev/null +++ b/src/tests/apps/miniterminal/Console.h @@ -0,0 +1,96 @@ +#ifndef _CONSOLE_H_ +#define _CONSOLE_H_ + +#include + +#define TAB_SIZE 8 +#define TAB_MASK 7 + +#define FMASK 0x0f +#define BMASK 0x70 + +typedef enum { + CONSOLE_STATE_NORMAL = 0, + CONSOLE_STATE_GOT_ESCAPE, + CONSOLE_STATE_SEEN_BRACKET, + CONSOLE_STATE_NEW_ARG, + CONSOLE_STATE_PARSING_ARG, +} console_state; + +typedef enum { + SCREEN_ERASE_WHOLE, + SCREEN_ERASE_UP, + SCREEN_ERASE_DOWN +} erase_screen_mode; + +typedef enum { + LINE_ERASE_WHOLE, + LINE_ERASE_LEFT, + LINE_ERASE_RIGHT +} erase_line_mode; + +#define MAX_ARGS 8 + +class ViewBuffer; + +class Console { +public: + Console(ViewBuffer *output); + ~Console(); + + void ResetConsole(); + + void SetScrollRegion(int top, int bottom); + void ScrollUp(); + void ScrollDown(); + + void LineFeed(); + void RLineFeed(); + void CariageReturn(); + + void Delete(); + void Tab(); + + void EraseLine(erase_line_mode mode); + void EraseScreen(erase_screen_mode mode); + + void SaveCursor(bool save_attrs); + void RestoreCursor(bool restore_attrs); + void UpdateCursor(int x, int y); + void GotoXY(int new_x, int new_y); + + void PutChar(const char c); + + void SetVT100Attributes(int32 *args, int32 argCount); + bool ProcessVT100Command(const char c, bool seen_bracket, int32 *args, int32 argCount); + + void Write(const void *buf, size_t len); + +private: +static void ResizeCallback(int32 width, int32 height, void *data); + + int32 fLines; + int32 fColumns; + + uint8 fAttr; + uint8 fSavedAttr; + bool fBrightAttr; + bool fReverseAttr; + + int32 fX; // current x coordinate + int32 fY; // current y coordinate + int32 fSavedX; // used to save x and y + int32 fSavedY; + + int32 fScrollTop; // top of the scroll region + int32 fScrollBottom; // bottom of the scroll region + + /* state machine */ + console_state fState; + int32 fArgCount; + int32 fArgs[MAX_ARGS]; + + ViewBuffer *fOutput; +}; + +#endif diff --git a/src/tests/apps/miniterminal/Jamfile b/src/tests/apps/miniterminal/Jamfile new file mode 100644 index 0000000000..20f0d0a5b8 --- /dev/null +++ b/src/tests/apps/miniterminal/Jamfile @@ -0,0 +1,13 @@ +SubDir OBOS_TOP src tests apps miniterminal ; + +UseHeaders [ FDirName $(OBOS_TOP) src tests apps miniterminal ] ; +UseHeaders [ FDirName $(OBOS_TOP) src apps terminal MYOB ] ; + +App MiniTerminal : + MiniApp.cpp + MiniWin.cpp + MiniView.cpp + Console.cpp + ViewBuffer.cpp ; + +LinkSharedOSLibs MiniTerminal : libbe.so ; diff --git a/src/tests/apps/miniterminal/MiniApp.cpp b/src/tests/apps/miniterminal/MiniApp.cpp new file mode 100644 index 0000000000..0e665800cc --- /dev/null +++ b/src/tests/apps/miniterminal/MiniApp.cpp @@ -0,0 +1,39 @@ +/* + * Part of the MiniTerminal. + * + * Copyright 2005 Michael Lotz. All rights reserved. + * Distributed under the Haiku License. + */ + +#include "MiniApp.h" +#include "MiniWin.h" +#include "MiniView.h" + +MiniApp::MiniApp() + : BApplication("application/x-vnd.Haiku.MiniTerminal") +{ + fWindow = new MiniWin(BRect(50, 50, 630, 435)); + fWindow->Show(); +} + + +void +MiniApp::ReadyToRun() +{ + fWindow->View()->Start(); +} + + +MiniApp::~MiniApp() +{ +} + + +int +main(void) +{ + MiniApp *app = new MiniApp(); + app->Run(); + delete app; + return 0; +} diff --git a/src/tests/apps/miniterminal/MiniApp.h b/src/tests/apps/miniterminal/MiniApp.h new file mode 100644 index 0000000000..58dbc92cdb --- /dev/null +++ b/src/tests/apps/miniterminal/MiniApp.h @@ -0,0 +1,19 @@ +#ifndef _MINI_APP_H_ +#define _MINI_APP_H_ + +#include + +class MiniWin; + +class MiniApp : public BApplication { +public: + MiniApp(); +virtual ~MiniApp(); + +virtual void ReadyToRun(); + +private: + MiniWin *fWindow; +}; + +#endif \ No newline at end of file diff --git a/src/tests/apps/miniterminal/MiniView.cpp b/src/tests/apps/miniterminal/MiniView.cpp new file mode 100644 index 0000000000..e8c15986cb --- /dev/null +++ b/src/tests/apps/miniterminal/MiniView.cpp @@ -0,0 +1,306 @@ +/* + * MiniTerminal - A basic windowed terminal to allow + * command-line interaction from within app_server. + * Based on consoled and MuTerminal. + * + * Copyright 2005 Michael Lotz. All rights reserved. + * Distributed under the Haiku License. + * + * Copyright 2004-2005, Haiku. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Copyright 2002, Travis Geiselbrecht. All rights reserved. + * Distributed under the terms of the NewOS License. + */ + + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "MiniView.h" +#include "Console.h" + +#include "VTkeymap.h" + +//#define TRACE_MINI_TERMINAL +#ifdef TRACE_MINI_TERMINAL + #ifdef __HAIKU__ + #define TRACE(x) debug_printf x + #else + #define TRACE(x) printf x + #endif +#else + #define TRACE(x) +#endif + +void +Setenv(const char *var, const char *value) +{ + int envindex = 0; + const int len = strlen(var); + const int val_len = strlen (value); + + while (environ [envindex] != NULL) { + if (strncmp (environ [envindex], var, len) == 0) { + /* found it */ + environ[envindex] = (char *)malloc ((unsigned)len + val_len + 1); + sprintf (environ [envindex], "%s%s", var, value); + return; + } + envindex ++; + } + + environ [envindex] = (char *) malloc ((unsigned)len + val_len + 1); + sprintf (environ [envindex], "%s%s", var, value); + environ [++envindex] = NULL; +} + +static int32 +ConsoleWriter(void *arg) +{ + char buf[1024]; + ssize_t len; + MiniView *view = (MiniView *)arg; + + for (;;) { + len = read(view->fMasterFD, buf, sizeof(buf)); + if (len < 0) + break; + + view->fConsole->Write(buf, len); + } + + return 0; +} + +static int32 +ExecuteShell(void *arg) +{ + MiniView *view = (MiniView *)arg; + + for (;;) { + const char *argv[3]; + argv[0] = "/bin/sh"; + argv[1] = "--login"; + argv[2] = NULL; + + int saved_stdin = dup(0); + int saved_stdout = dup(1); + int saved_stderr = dup(2); + + dup2(view->fSlaveFD, 0); + dup2(view->fSlaveFD, 1); + dup2(view->fSlaveFD, 2); + + thread_id shell = load_image(2, argv, (const char **)environ); + setpgid(shell, 0); + + status_t return_code; + wait_for_thread(shell, &return_code); + + dup2(saved_stdin, 0); + dup2(saved_stdout, 1); + dup2(saved_stderr, 2); + close(saved_stdin); + close(saved_stdout); + close(saved_stderr); + } + + return B_OK; +} + +MiniView::MiniView(BRect frame) + : ViewBuffer(frame) +{ +} + +MiniView::~MiniView() +{ +} + +void +MiniView::Start() +{ + fConsole = new Console(this); + + if (OpenTTY() != B_OK) { + TRACE(("error in OpenTTY\n")); + return; + } + + // we're a session leader + setsid(); + + // move our stdin and stdout to the console + dup2(fSlaveFD, 0); + dup2(fSlaveFD, 1); + dup2(fSlaveFD, 2); + + if (SpawnThreads() != B_OK) + TRACE(("error in SpawnThreads\n")); +} + +status_t +MiniView::OpenTTY() +{ + DIR *dir; + + dir = opendir("/dev/pt"); + if (dir != NULL) { + struct dirent *entry; + char name[64]; + + while ((entry = readdir(dir)) != NULL) { + if (entry->d_name[0] == '.') // filter out . and .. + continue; + + sprintf(name, "/dev/pt/%s", entry->d_name); + + fMasterFD = open(name, O_RDWR); + if (fMasterFD >= 0) { + sprintf(name, "/dev/tt/%s", entry->d_name); + + fSlaveFD = open(name, O_RDWR); + if (fSlaveFD < 0) { + TRACE(("cannot open tty\n")); + close(fMasterFD); + } else { + struct termios tio; + tcgetattr(fSlaveFD, &tio); + + // set signal default + signal(SIGCHLD, SIG_DFL); + signal(SIGHUP, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + signal(SIGTERM, SIG_DFL); + signal(SIGINT, SIG_DFL); + signal(SIGTTOU, SIG_DFL); + + // set terminal interface + tio.c_line = 0; + tio.c_lflag |= ECHOE; + + // input: nl->nl, cr->nl + tio.c_iflag &= ~(INLCR|IGNCR); + tio.c_iflag |= ICRNL; + tio.c_iflag &= ~ISTRIP; + + // output: cr->cr, nl in not retrun, no delays, ln->cr/ln + tio.c_oflag &= ~(OCRNL|ONLRET|NLDLY|CRDLY|TABDLY|BSDLY|VTDLY|FFDLY); + tio.c_oflag |= ONLCR; + tio.c_oflag |= OPOST; + + // baud rate is 19200 (equal to beterm) + tio.c_cflag &= ~(CBAUD); + tio.c_cflag |= B19200; + + tio.c_cflag &= ~CSIZE; + tio.c_cflag |= CS8; + tio.c_cflag |= CREAD; + + tio.c_cflag |= HUPCL; + tio.c_iflag &= ~(IGNBRK|BRKINT); + + // enable signals, canonical processing (erase, kill, etc), echo + tio.c_lflag |= ISIG|ICANON|ECHO|ECHOE|ECHONL; + tio.c_lflag &= ~ECHOK; + + tio.c_lflag &= ~IEXTEN; + + // set control charactors + tio.c_cc[VINTR] = 'C' & 0x1f; /* '^C' */ + tio.c_cc[VQUIT] = '\\'& 0x1f; /* '^\' */ + tio.c_cc[VERASE] = 0x08; /* '^H' */ + tio.c_cc[VKILL] = 'U' & 0x1f; /* '^U' */ + tio.c_cc[VEOF] = 'D' & 0x1f; /* '^D' */ + tio.c_cc[VEOL] = 0; /* '^@' */ + tio.c_cc[VMIN] = 4; + tio.c_cc[VTIME] = 0; + tio.c_cc[VEOL2] = 0; /* '^@' */ + tio.c_cc[VSWTCH] = 0; /* '^@' */ + tio.c_cc[VSTART] = 'S' & 0x1f; /* '^S' */ + tio.c_cc[VSTOP] = 'Q' & 0x1f; /* '^Q' */ + tio.c_cc[VSUSP] = '@' & 0x1f; /* '^@' */ + + // set terminal interface + tcsetattr(fSlaveFD, TCSANOW, &tio); + + // set window size (currently disabled) + /*ws.ws_row = rows; + ws.ws_col = cols; + + ioctl(con->tty_slave_fd, TIOCSWINSZ, &ws);*/ + } + break; + } + } + + + Setenv("TTY", name); + } + + if (fMasterFD < 0 || fSlaveFD < 0) { + TRACE(("could not open master or slave fd\n")); + return B_ERROR; + } + + Setenv("TERM", "beterm"); + return B_OK; +} + +void +MiniView::KeyDown(const char *bytes, int32 numBytes) +{ + // TODO: add interrupt char handling + uint32 mod = modifiers(); + if (numBytes == 1) { + if (mod & B_OPTION_KEY) { + char c = bytes[0] | 0x80; + write(fMasterFD, &c, 1); + } else { + switch (bytes[0]) { + case B_LEFT_ARROW: + write(fMasterFD, LEFT_ARROW_KEY_CODE, sizeof(LEFT_ARROW_KEY_CODE)); + break; + case B_RIGHT_ARROW: + write(fMasterFD, RIGHT_ARROW_KEY_CODE, sizeof(RIGHT_ARROW_KEY_CODE)); + break; + case B_UP_ARROW: + write(fMasterFD, UP_ARROW_KEY_CODE, sizeof(UP_ARROW_KEY_CODE)); + break; + case B_DOWN_ARROW: + write(fMasterFD, DOWN_ARROW_KEY_CODE, sizeof(DOWN_ARROW_KEY_CODE)); + break; + default: + write(fMasterFD, bytes, numBytes); + } + } + } else + write(fMasterFD, bytes, numBytes); +} + +status_t +MiniView::SpawnThreads() +{ + fConsoleWriter = spawn_thread(&ConsoleWriter, "console writer", B_URGENT_DISPLAY_PRIORITY, this); + if (fConsoleWriter < 0) + return B_ERROR; + TRACE(("console writer thread is: %d\n", fConsoleWriter)); + + fShellProcess = spawn_thread(&ExecuteShell, "shell process", B_URGENT_DISPLAY_PRIORITY, this); + if (fShellProcess < 0) + return B_ERROR; + TRACE(("shell process thread is: %d\n", fShellProcess)); + + resume_thread(fConsoleWriter); + resume_thread(fShellProcess); + return B_OK; +} diff --git a/src/tests/apps/miniterminal/MiniView.h b/src/tests/apps/miniterminal/MiniView.h new file mode 100644 index 0000000000..6973a8c147 --- /dev/null +++ b/src/tests/apps/miniterminal/MiniView.h @@ -0,0 +1,24 @@ +#ifndef _MINI_TERMINAL_H_ +#define _MINI_TERMINAL_H_ + +#include "ViewBuffer.h" +#include "Console.h" + +class MiniView : public ViewBuffer { +public: + MiniView(BRect frame); +virtual ~MiniView(); + + void Start(); + status_t OpenTTY(); + status_t SpawnThreads(); +virtual void KeyDown(const char *bytes, int32 numBytes); + + Console *fConsole; + int fMasterFD; + int fSlaveFD; + thread_id fShellProcess; + thread_id fConsoleWriter; +}; + +#endif diff --git a/src/tests/apps/miniterminal/MiniWin.cpp b/src/tests/apps/miniterminal/MiniWin.cpp new file mode 100644 index 0000000000..c6595d1eff --- /dev/null +++ b/src/tests/apps/miniterminal/MiniWin.cpp @@ -0,0 +1,29 @@ +/* + * Part of the MiniTerminal. + * + * Copyright 2005 Michael Lotz. All rights reserved. + * Distributed under the Haiku License. + */ + +#include "MiniWin.h" +#include "MiniView.h" + +MiniWin::MiniWin(BRect bounds) + : BWindow(bounds, "MiniTerminal", B_TITLED_WINDOW, B_QUIT_ON_WINDOW_CLOSE) +{ + fView = new MiniView(bounds.OffsetToSelf(0, 0)); + AddChild(fView); + fView->MakeFocus(); +} + + +MiniWin::~MiniWin() +{ +} + + +MiniView * +MiniWin::View() +{ + return fView; +} diff --git a/src/tests/apps/miniterminal/MiniWin.h b/src/tests/apps/miniterminal/MiniWin.h new file mode 100644 index 0000000000..0718bab74d --- /dev/null +++ b/src/tests/apps/miniterminal/MiniWin.h @@ -0,0 +1,19 @@ +#ifndef _MINI_WIN_H_ +#define _MINI_WIN_H_ + +#include + +class MiniView; + +class MiniWin : public BWindow { +public: + MiniWin(BRect bounds); +virtual ~MiniWin(); + + MiniView *View(); + +private: + MiniView *fView; +}; + +#endif diff --git a/src/tests/apps/miniterminal/ViewBuffer.cpp b/src/tests/apps/miniterminal/ViewBuffer.cpp new file mode 100644 index 0000000000..94d1d0a8b1 --- /dev/null +++ b/src/tests/apps/miniterminal/ViewBuffer.cpp @@ -0,0 +1,232 @@ +/* + * ViewBuffer - Mimicing a frame buffer output - but using a BView. + * Based on frame_buffer_console. + * + * Copyright 2005 Michael Lotz. All rights reserved. + * Distributed under the Haiku License. + * + * Copyright 2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ + +#include "ViewBuffer.h" + +#define CHAR_WIDTH 7 +#define CHAR_HEIGHT 12 + +// Palette is (black and white are swapt like in normal BeOS Terminal) +// 0 - black, +// 1 - blue, +// 2 - green, +// 3 - cyan, +// 4 - red, +// 5 - magenta, +// 6 - yellow, +// 7 - white +// 8-15 - same but bright (we're ignoring those) + +static uint32 sPalette32[] = { + 0xffffff, + 0x0000ff, + 0x00ff00, + 0x00ffff, + 0xff0000, + 0xff00ff, + 0xffff00, + 0x000000, +}; + + +ViewBuffer::ViewBuffer(BRect frame) + : BView(frame, "ViewBuffer", B_FOLLOW_ALL, B_WILL_DRAW | B_FRAME_EVENTS) +{ + SetFont(be_fixed_font); + + fColumns = frame.IntegerWidth() / CHAR_WIDTH; + fRows = frame.IntegerHeight() / CHAR_HEIGHT; + fResizeCallback = NULL; + + // initially, the cursor is hidden + fCursorX = -1; + fCursorY = -1; + + // initialize private palette + for (int i = 0; i < 8; i++) { + fPalette[i].red = (sPalette32[i] >> 16) & 0xff; + fPalette[i].green = (sPalette32[i] >> 8) & 0xff; + fPalette[i].blue = (sPalette32[i] >> 0) & 0xff; + fPalette[i].alpha = 0xff; + } +} + + +ViewBuffer::~ViewBuffer() +{ +} + + +void +ViewBuffer::FrameResized(float width, float height) +{ + fColumns = (int32)width / CHAR_WIDTH; + fRows = (int32)height / CHAR_HEIGHT; + + if (fResizeCallback) + fResizeCallback(fColumns, fRows, fResizeCallbackData); +} + + +status_t +ViewBuffer::GetSize(int32 *width, int32 *height) +{ + *width = fColumns; + *height = fRows; + return B_OK; +} + + +void +ViewBuffer::SetResizeCallback(resize_callback callback, void *data) +{ + fResizeCallback = callback; + fResizeCallbackData = data; +} + + +uint8 +ViewBuffer::ForegroundColor(uint8 attr) +{ + return attr & 0x7; +} + + +uint8 +ViewBuffer::BackgroundColor(uint8 attr) +{ + return (attr >> 4) & 0x7; +} + + +rgb_color +ViewBuffer::GetPaletteEntry(uint8 index) +{ + return fPalette[index]; +} + + +void +ViewBuffer::PutGlyph(int32 x, int32 y, uint8 glyph, uint8 attr) +{ + if (x >= fColumns || y >= fRows) + return; + + RenderGlyph(x, y, glyph, attr); +} + + +void +ViewBuffer::FillGlyph(int32 x, int32 y, int32 width, int32 height, uint8 glyph, uint8 attr) +{ + if (x >= fColumns || y >= fRows) + return; + + int32 left = x + width; + if (left > fColumns) + left = fColumns; + + int32 bottom = y + height; + if (bottom > fRows) + bottom = fRows; + + for (; y < bottom; y++) { + for (int32 x2 = x; x2 < left; x2++) { + RenderGlyph(x2, y, glyph, attr); + } + } +} + + +void +ViewBuffer::RenderGlyph(int32 x, int32 y, uint8 glyph, uint8 attr) +{ + BPoint where(x * CHAR_WIDTH, (y + 1) * CHAR_HEIGHT - 3); + + char string[2]; + string[0] = glyph; + string[1] = 0; + + if (LockLooper()) { + SetHighColor(GetPaletteEntry(ForegroundColor(attr))); + SetLowColor(GetPaletteEntry(BackgroundColor(attr))); + FillRect(BRect(x * CHAR_WIDTH, y * CHAR_HEIGHT, (x + 1) * CHAR_WIDTH, (y + 1) * CHAR_HEIGHT), B_SOLID_LOW); + DrawString(string, where); + Sync(); + UnlockLooper(); + } +} + + +void +ViewBuffer::DrawCursor(int32 x, int32 y) +{ + if (x < 0 || y < 0) + return; + + x *= CHAR_WIDTH; + y *= CHAR_HEIGHT; + + if (LockLooper()) { + InvertRect(BRect(x, y, x + CHAR_WIDTH, y + CHAR_HEIGHT)); + Sync(); + UnlockLooper(); + } +} + + +void +ViewBuffer::MoveCursor(int32 x, int32 y) +{ + DrawCursor(fCursorX, fCursorY); + DrawCursor(x, y); + + fCursorX = x; + fCursorY = y; +} + + +void +ViewBuffer::Blit(int32 srcx, int32 srcy, int32 width, int32 height, int32 destx, int32 desty) +{ + height *= CHAR_HEIGHT; + width *= CHAR_WIDTH; + + srcx *= CHAR_WIDTH; + srcy *= CHAR_HEIGHT; + BRect source(srcx, srcy, srcx + width, srcy + height); + + destx *= CHAR_WIDTH; + desty *= CHAR_HEIGHT; + BRect dest(destx, desty, destx + width, desty + height); + + if (LockLooper()) { + CopyBits(source, dest); + Sync(); + UnlockLooper(); + } +} + + +void +ViewBuffer::Clear(uint8 attr) +{ + if (LockLooper()) { + SetLowColor(GetPaletteEntry(BackgroundColor(attr))); + SetViewColor(LowColor()); + FillRect(Frame(), B_SOLID_LOW); + Sync(); + UnlockLooper(); + } + + fCursorX = -1; + fCursorY = -1; +} diff --git a/src/tests/apps/miniterminal/ViewBuffer.h b/src/tests/apps/miniterminal/ViewBuffer.h new file mode 100644 index 0000000000..942f6bc279 --- /dev/null +++ b/src/tests/apps/miniterminal/ViewBuffer.h @@ -0,0 +1,43 @@ +#ifndef _VIEW_BUFFER_H_ +#define _VIEW_BUFFER_H_ + +#include +#include + +typedef void (*resize_callback)(int32 width, int32 height, void *data); + +class ViewBuffer : public BView { +public: + ViewBuffer(BRect frame); +virtual ~ViewBuffer(); + +virtual void FrameResized(float new_width, float new_height); + void SetResizeCallback(resize_callback callback, void *data); + status_t GetSize(int32 *width, int32 *height); + + uint8 ForegroundColor(uint8 attr); + uint8 BackgroundColor(uint8 attr); + rgb_color GetPaletteEntry(uint8 index); + + void PutGlyph(int32 x, int32 y, uint8 glyph, uint8 attr); + void FillGlyph(int32 x, int32 y, int32 width, int32 height, uint8 glyph, uint8 attr); + void RenderGlyph(int32 x, int32 y, uint8 glyph, uint8 attr); + + void DrawCursor(int32 x, int32 y); + void MoveCursor(int32 x, int32 y); + + void Blit(int32 srcx, int32 srcy, int32 width, int32 height, int32 destx, int32 desty); + void Clear(uint8 attr); + +private: + int32 fColumns; + int32 fRows; + resize_callback fResizeCallback; + void *fResizeCallbackData; + int32 fCursorX; + int32 fCursorY; + + rgb_color fPalette[8]; +}; + +#endif