1/*
2 * Copyright 2013-2023, Haiku, Inc. All rights reserved.
3 * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
4 * Distributed under the terms of the MIT License.
5 *
6 * Authors:
7 *		Ingo Weinhold, ingo_weinhold@gmx.de
8 *		Simon South, simon@simonsouth.net
9 *		Siarzhuk Zharski, zharik@gmx.li
10 */
11
12#include "TerminalBuffer.h"
13
14#include <algorithm>
15
16#include <Message.h>
17
18#include "Colors.h"
19#include "TermApp.h"
20#include "TermConst.h"
21
22
23// #pragma mark - public methods
24
25
26TerminalBuffer::TerminalBuffer()
27	:
28	BLocker("terminal buffer"),
29	fEncoding(M_UTF8),
30	fAlternateScreen(NULL),
31	fAlternateHistory(NULL),
32	fAlternateScreenOffset(0),
33	fAlternateAttributes(),
34	fColorsPalette(NULL),
35	fListenerValid(false)
36{
37}
38
39
40TerminalBuffer::~TerminalBuffer()
41{
42	free(fAlternateScreen);
43	delete fAlternateHistory;
44	delete[] fColorsPalette;
45}
46
47
48status_t
49TerminalBuffer::Init(int32 width, int32 height, int32 historySize)
50{
51	if (BLocker::InitCheck() < 0)
52		return BLocker::InitCheck();
53
54	fAlternateScreen = _AllocateLines(width, height);
55	if (fAlternateScreen == NULL)
56		return B_NO_MEMORY;
57
58	for (int32 i = 0; i < height; i++)
59		fAlternateScreen[i]->Clear();
60
61	fColorsPalette = new(std::nothrow) rgb_color[kTermColorCount];
62	if (fColorsPalette == NULL)
63		return B_NO_MEMORY;
64
65	for (uint i = 0; i < kTermColorCount; i++)
66		fColorsPalette[i] = TermApp::DefaultPalette()[i];
67
68	return BasicTerminalBuffer::Init(width, height, historySize);
69}
70
71
72void
73TerminalBuffer::SetListener(BMessenger listener)
74{
75	fListener = listener;
76	fListenerValid = true;
77}
78
79
80void
81TerminalBuffer::UnsetListener()
82{
83	fListenerValid = false;
84}
85
86
87int
88TerminalBuffer::Encoding() const
89{
90	return fEncoding;
91}
92
93
94void
95TerminalBuffer::EnableInterpretMetaKey(bool enable)
96{
97	if (fListenerValid) {
98		BMessage message(MSG_ENABLE_META_KEY);
99		message.AddBool("enableInterpretMetaKey", enable);
100		fListener.SendMessage(&message);
101	}
102}
103
104
105void
106TerminalBuffer::EnableMetaKeySendsEscape(bool enable)
107{
108	if (fListenerValid) {
109		BMessage message(MSG_ENABLE_META_KEY);
110		message.AddBool("enableMetaKeySendsEscape", enable);
111		fListener.SendMessage(&message);
112	}
113}
114
115
116void
117TerminalBuffer::EnableBracketedPasteMode(bool enable)
118{
119	if (fListenerValid) {
120		BMessage message(MSG_ENABLE_BRACKETED_PASTE);
121		message.AddBool("enableBracketedPaste", enable);
122		fListener.SendMessage(&message);
123	}
124}
125
126
127void
128TerminalBuffer::ReportX10MouseEvent(bool reportX10MouseEvent)
129{
130	if (fListenerValid) {
131		BMessage message(MSG_REPORT_MOUSE_EVENT);
132		message.AddBool("reportX10MouseEvent", reportX10MouseEvent);
133		fListener.SendMessage(&message);
134	}
135}
136
137
138void
139TerminalBuffer::ReportNormalMouseEvent(bool reportNormalMouseEvent)
140{
141	if (fListenerValid) {
142		BMessage message(MSG_REPORT_MOUSE_EVENT);
143		message.AddBool("reportNormalMouseEvent", reportNormalMouseEvent);
144		fListener.SendMessage(&message);
145	}
146}
147
148
149void
150TerminalBuffer::ReportButtonMouseEvent(bool report)
151{
152	if (fListenerValid) {
153		BMessage message(MSG_REPORT_MOUSE_EVENT);
154		message.AddBool("reportButtonMouseEvent", report);
155		fListener.SendMessage(&message);
156	}
157}
158
159
160void
161TerminalBuffer::ReportAnyMouseEvent(bool reportAnyMouseEvent)
162{
163	if (fListenerValid) {
164		BMessage message(MSG_REPORT_MOUSE_EVENT);
165		message.AddBool("reportAnyMouseEvent", reportAnyMouseEvent);
166		fListener.SendMessage(&message);
167	}
168}
169
170
171void
172TerminalBuffer::EnableExtendedMouseCoordinates(bool enable)
173{
174	if (fListenerValid) {
175		BMessage message(MSG_REPORT_MOUSE_EVENT);
176		message.AddBool("enableExtendedMouseCoordinates", enable);
177		fListener.SendMessage(&message);
178	}
179}
180
181
182void
183TerminalBuffer::SetEncoding(int encoding)
184{
185	fEncoding = encoding;
186}
187
188
189void
190TerminalBuffer::SetTitle(const char* title)
191{
192	if (fListenerValid) {
193		BMessage message(MSG_SET_TERMINAL_TITLE);
194		message.AddString("title", title);
195		fListener.SendMessage(&message);
196	}
197}
198
199
200void
201TerminalBuffer::SetColors(uint8* indexes, rgb_color* colors,
202		int32 count, bool dynamic)
203{
204	if (fListenerValid) {
205		BMessage message(MSG_SET_TERMINAL_COLORS);
206		message.AddInt32("count", count);
207		message.AddBool("dynamic", dynamic);
208		message.AddData("index", B_UINT8_TYPE,
209					indexes, sizeof(uint8), true, count);
210		message.AddData("color", B_RGB_COLOR_TYPE,
211					colors, sizeof(rgb_color), true, count);
212
213		for (int i = 1; i < count; i++) {
214			message.AddData("index", B_UINT8_TYPE, &indexes[i], sizeof(uint8));
215			message.AddData("color", B_RGB_COLOR_TYPE, &colors[i],
216					sizeof(rgb_color));
217		}
218
219		fListener.SendMessage(&message);
220	}
221}
222
223
224void
225TerminalBuffer::ResetColors(uint8* indexes, int32 count, bool dynamic)
226{
227	if (fListenerValid) {
228		BMessage message(MSG_RESET_TERMINAL_COLORS);
229		message.AddInt32("count", count);
230		message.AddBool("dynamic", dynamic);
231		message.AddData("index", B_UINT8_TYPE,
232					indexes, sizeof(uint8), true, count);
233
234		for (int i = 1; i < count; i++)
235			message.AddData("index", B_UINT8_TYPE, &indexes[i], sizeof(uint8));
236
237		fListener.SendMessage(&message);
238	}
239}
240
241
242void
243TerminalBuffer::GetColor(uint8 index)
244{
245	if (fListenerValid) {
246		BMessage message(MSG_GET_TERMINAL_COLOR);
247		message.AddUInt8("index", index);
248		fListener.SendMessage(&message);
249	}
250}
251
252
253void
254TerminalBuffer::SetCursorStyle(int32 style, bool blinking)
255{
256	if (fListenerValid) {
257		BMessage message(MSG_SET_CURSOR_STYLE);
258		message.AddInt32("style", style);
259		message.AddBool("blinking", blinking);
260		fListener.SendMessage(&message);
261	}
262}
263
264
265void
266TerminalBuffer::SetCursorBlinking(bool blinking)
267{
268	if (fListenerValid) {
269		BMessage message(MSG_SET_CURSOR_STYLE);
270		message.AddBool("blinking", blinking);
271		fListener.SendMessage(&message);
272	}
273}
274
275
276void
277TerminalBuffer::SetCursorHidden(bool hidden)
278{
279	if (fListenerValid) {
280		BMessage message(MSG_SET_CURSOR_STYLE);
281		message.AddBool("hidden", hidden);
282		fListener.SendMessage(&message);
283	}
284}
285
286
287void
288TerminalBuffer::SetPaletteColor(uint8 index, rgb_color color)
289{
290	fColorsPalette[index] = color;
291}
292
293
294void
295TerminalBuffer::NotifyQuit(int32 reason)
296{
297	if (fListenerValid) {
298		BMessage message(MSG_QUIT_TERMNAL);
299		message.AddInt32("reason", reason);
300		fListener.SendMessage(&message);
301	}
302}
303
304
305void
306TerminalBuffer::NotifyListener()
307{
308	if (fListenerValid)
309		fListener.SendMessage(MSG_TERMINAL_BUFFER_CHANGED);
310}
311
312
313status_t
314TerminalBuffer::ResizeTo(int32 width, int32 height)
315{
316	int32 historyCapacity = 0;
317	if (!fAlternateScreenActive)
318		historyCapacity = HistoryCapacity();
319	else if (fAlternateHistory != NULL)
320		historyCapacity = fAlternateHistory->Capacity();
321
322	return ResizeTo(width, height, historyCapacity);
323}
324
325
326status_t
327TerminalBuffer::ResizeTo(int32 width, int32 height, int32 historyCapacity)
328{
329	// switch to the normal screen buffer first
330	bool alternateScreenActive = fAlternateScreenActive;
331	if (alternateScreenActive)
332		_SwitchScreenBuffer();
333
334	int32 oldWidth = fWidth;
335	int32 oldHeight = fHeight;
336
337	// Resize the normal screen buffer/history.
338	status_t error = BasicTerminalBuffer::ResizeTo(width, height,
339		historyCapacity);
340	if (error != B_OK) {
341		if (alternateScreenActive)
342			_SwitchScreenBuffer();
343		return error;
344	}
345
346	// Switch to the alternate screen buffer and resize it.
347	if (fAlternateScreen != NULL) {
348		TermPos cursor = fCursor;
349		fCursor.SetTo(0, 0);
350		fWidth = oldWidth;
351		fHeight = oldHeight;
352
353		_SwitchScreenBuffer();
354
355		error = BasicTerminalBuffer::ResizeTo(width, height, 0);
356
357		fWidth = width;
358		fHeight = height;
359		fCursor = cursor;
360
361		// Switch back.
362		if (!alternateScreenActive)
363			_SwitchScreenBuffer();
364
365		if (error != B_OK) {
366			// This sucks -- we can't do anything about it. Delete the
367			// alternate screen buffer.
368			_FreeLines(fAlternateScreen, oldHeight);
369			fAlternateScreen = NULL;
370		}
371	}
372
373	return error;
374}
375
376
377void
378TerminalBuffer::UseAlternateScreenBuffer(bool clear)
379{
380	if (fAlternateScreenActive || fAlternateScreen == NULL)
381		return;
382
383	_SwitchScreenBuffer();
384
385	if (clear)
386		Clear(false);
387
388	_InvalidateAll();
389}
390
391
392void
393TerminalBuffer::UseNormalScreenBuffer()
394{
395	if (!fAlternateScreenActive)
396		return;
397
398	_SwitchScreenBuffer();
399	_InvalidateAll();
400}
401
402
403void
404TerminalBuffer::_SwitchScreenBuffer()
405{
406	std::swap(fScreen, fAlternateScreen);
407	std::swap(fHistory, fAlternateHistory);
408	std::swap(fScreenOffset, fAlternateScreenOffset);
409	std::swap(fAttributes, fAlternateAttributes);
410	fAlternateScreenActive = !fAlternateScreenActive;
411}
412