/* * Copyright 2013-2023, Haiku, Inc. All rights reserved. * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. * Distributed under the terms of the MIT License. * * Authors: * Ingo Weinhold, ingo_weinhold@gmx.de * Simon South, simon@simonsouth.net * Siarzhuk Zharski, zharik@gmx.li */ #include "TerminalBuffer.h" #include #include #include "Colors.h" #include "TermApp.h" #include "TermConst.h" // #pragma mark - public methods TerminalBuffer::TerminalBuffer() : BLocker("terminal buffer"), fEncoding(M_UTF8), fAlternateScreen(NULL), fAlternateHistory(NULL), fAlternateScreenOffset(0), fAlternateAttributes(), fColorsPalette(NULL), fListenerValid(false), fMode(MODE_INTERPRET_META_KEY | MODE_META_KEY_SENDS_ESCAPE | MODE_CURSOR_BLINKING), fCursorStyle(BLOCK_CURSOR), fNextOSCRef(1) { } TerminalBuffer::~TerminalBuffer() { free(fAlternateScreen); delete fAlternateHistory; delete[] fColorsPalette; HyperLinkRefMap::Iterator iterator = fHyperLinkForRef.GetIterator(); while (iterator.HasNext()) { HyperLinkRefMap::Entry entry = iterator.Next(); delete entry.value; } fHyperLinkForID.Clear(); fHyperLinkForRef.Clear(); } status_t TerminalBuffer::Init(int32 width, int32 height, int32 historySize) { if (BLocker::InitCheck() < 0) return BLocker::InitCheck(); fAlternateScreen = _AllocateLines(width, height); if (fAlternateScreen == NULL) return B_NO_MEMORY; for (int32 i = 0; i < height; i++) fAlternateScreen[i]->Clear(); fColorsPalette = new(std::nothrow) rgb_color[kTermColorCount]; if (fColorsPalette == NULL) return B_NO_MEMORY; for (uint i = 0; i < kTermColorCount; i++) fColorsPalette[i] = TermApp::DefaultPalette()[i]; return BasicTerminalBuffer::Init(width, height, historySize); } void TerminalBuffer::SetListener(BMessenger listener) { fListener = listener; fListenerValid = true; } void TerminalBuffer::UnsetListener() { fListenerValid = false; } int TerminalBuffer::Encoding() const { return fEncoding; } bool TerminalBuffer::IsMode(uint32 mode) const { return (fMode & mode) != 0; } void TerminalBuffer::SetMode(uint32 mode) { fMode |= mode; if (fListenerValid && (mode & MODE_CURSOR_BLINKING) != 0) fListener.SendMessage(MSG_SET_CURSOR_STYLE); } void TerminalBuffer::ResetMode(uint32 mode) { fMode &= ~mode; if (fListenerValid && (mode & MODE_CURSOR_BLINKING) != 0) fListener.SendMessage(MSG_SET_CURSOR_STYLE); } void TerminalBuffer::SetEncoding(int encoding) { fEncoding = encoding; } void TerminalBuffer::SetTitle(const char* title) { if (fListenerValid) { BMessage message(MSG_SET_TERMINAL_TITLE); message.AddString("title", title); fListener.SendMessage(&message); } } void TerminalBuffer::SetColors(uint8* indexes, rgb_color* colors, int32 count, bool dynamic) { if (fListenerValid) { BMessage message(MSG_SET_TERMINAL_COLORS); message.AddInt32("count", count); message.AddBool("dynamic", dynamic); message.AddData("index", B_UINT8_TYPE, indexes, sizeof(uint8), true, count); message.AddData("color", B_RGB_COLOR_TYPE, colors, sizeof(rgb_color), true, count); for (int i = 1; i < count; i++) { message.AddData("index", B_UINT8_TYPE, &indexes[i], sizeof(uint8)); message.AddData("color", B_RGB_COLOR_TYPE, &colors[i], sizeof(rgb_color)); } fListener.SendMessage(&message); } } void TerminalBuffer::ResetColors(uint8* indexes, int32 count, bool dynamic) { if (fListenerValid) { BMessage message(MSG_RESET_TERMINAL_COLORS); message.AddInt32("count", count); message.AddBool("dynamic", dynamic); message.AddData("index", B_UINT8_TYPE, indexes, sizeof(uint8), true, count); for (int i = 1; i < count; i++) message.AddData("index", B_UINT8_TYPE, &indexes[i], sizeof(uint8)); fListener.SendMessage(&message); } } void TerminalBuffer::GetColor(uint8 index) { if (fListenerValid) { BMessage message(MSG_GET_TERMINAL_COLOR); message.AddUInt8("index", index); fListener.SendMessage(&message); } } void TerminalBuffer::SetCursorStyle(int32 style) { fCursorStyle = style; } void TerminalBuffer::SetPaletteColor(uint8 index, rgb_color color) { fColorsPalette[index] = color; } void TerminalBuffer::NotifyQuit(int32 reason) { if (fListenerValid) { BMessage message(MSG_QUIT_TERMNAL); message.AddInt32("reason", reason); fListener.SendMessage(&message); } } void TerminalBuffer::NotifyListener() { if (fListenerValid) fListener.SendMessage(MSG_TERMINAL_BUFFER_CHANGED); } status_t TerminalBuffer::ResizeTo(int32 width, int32 height) { int32 historyCapacity = 0; if (!fAlternateScreenActive) historyCapacity = HistoryCapacity(); else if (fAlternateHistory != NULL) historyCapacity = fAlternateHistory->Capacity(); return ResizeTo(width, height, historyCapacity); } status_t TerminalBuffer::ResizeTo(int32 width, int32 height, int32 historyCapacity) { // switch to the normal screen buffer first bool alternateScreenActive = fAlternateScreenActive; if (alternateScreenActive) _SwitchScreenBuffer(); int32 oldWidth = fWidth; int32 oldHeight = fHeight; // Resize the normal screen buffer/history. status_t error = BasicTerminalBuffer::ResizeTo(width, height, historyCapacity); if (error != B_OK) { if (alternateScreenActive) _SwitchScreenBuffer(); return error; } // Switch to the alternate screen buffer and resize it. if (fAlternateScreen != NULL) { TermPos cursor = fCursor; fCursor.SetTo(0, 0); fWidth = oldWidth; fHeight = oldHeight; _SwitchScreenBuffer(); error = BasicTerminalBuffer::ResizeTo(width, height, 0); fWidth = width; fHeight = height; fCursor = cursor; // Switch back. if (!alternateScreenActive) _SwitchScreenBuffer(); if (error != B_OK) { // This sucks -- we can't do anything about it. Delete the // alternate screen buffer. _FreeLines(fAlternateScreen, oldHeight); fAlternateScreen = NULL; } } return error; } void TerminalBuffer::UseAlternateScreenBuffer(bool clear) { if (fAlternateScreenActive || fAlternateScreen == NULL) return; _SwitchScreenBuffer(); if (clear) Clear(false); _InvalidateAll(); } void TerminalBuffer::UseNormalScreenBuffer() { if (!fAlternateScreenActive) return; _SwitchScreenBuffer(); _InvalidateAll(); } void TerminalBuffer::_SwitchScreenBuffer() { std::swap(fScreen, fAlternateScreen); std::swap(fHistory, fAlternateHistory); std::swap(fScreenOffset, fAlternateScreenOffset); std::swap(fAttributes, fAlternateAttributes); fAlternateScreenActive = !fAlternateScreenActive; } uint32 TerminalBuffer::PutHyperLink(const char* id, BString& uri) { HyperLink* hyperLink = NULL; if (id != NULL) hyperLink = fHyperLinkForID.Get(id); if (hyperLink == NULL) { hyperLink = new (std::nothrow) HyperLink(uri, fNextOSCRef++, id); if (id != NULL) fHyperLinkForID.Put(id, hyperLink); HyperLink* oldHyperLink = fHyperLinkForRef.Get(hyperLink->OSCRef()); fHyperLinkForRef.Put(hyperLink->OSCRef(), hyperLink); delete oldHyperLink; } return hyperLink->OSCRef(); } bool TerminalBuffer::GetHyperLink(uint32 ref, HyperLink &_link) { HyperLink* hyperLink = fHyperLinkForRef.Get(ref); if (hyperLink == NULL) { return false; } _link = *hyperLink; return true; }