* 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 <algorithm>
#include <Message.h>
#include "Colors.h"
#include "TermApp.h"
#include "TermConst.h"
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)
{
}
TerminalBuffer::~TerminalBuffer()
{
free(fAlternateScreen);
delete fAlternateHistory;
delete[] fColorsPalette;
}
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)
{
bool alternateScreenActive = fAlternateScreenActive;
if (alternateScreenActive)
_SwitchScreenBuffer();
int32 oldWidth = fWidth;
int32 oldHeight = fHeight;
status_t error = BasicTerminalBuffer::ResizeTo(width, height,
historyCapacity);
if (error != B_OK) {
if (alternateScreenActive)
_SwitchScreenBuffer();
return error;
}
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;
if (!alternateScreenActive)
_SwitchScreenBuffer();
if (error != B_OK) {
_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;
}