⛏️ index : haiku.git

/*
 * 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"


// #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)
{
}


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)
{
	// 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;
}