xref: /haiku/src/apps/terminal/TerminalBuffer.cpp (revision ed24eb5ff12640d052171c6a7feba37fab8a75d1)
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 
26 TerminalBuffer::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 
40 TerminalBuffer::~TerminalBuffer()
41 {
42 	free(fAlternateScreen);
43 	delete fAlternateHistory;
44 	delete[] fColorsPalette;
45 }
46 
47 
48 status_t
49 TerminalBuffer::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 
72 void
73 TerminalBuffer::SetListener(BMessenger listener)
74 {
75 	fListener = listener;
76 	fListenerValid = true;
77 }
78 
79 
80 void
81 TerminalBuffer::UnsetListener()
82 {
83 	fListenerValid = false;
84 }
85 
86 
87 int
88 TerminalBuffer::Encoding() const
89 {
90 	return fEncoding;
91 }
92 
93 
94 void
95 TerminalBuffer::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 
105 void
106 TerminalBuffer::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 
116 void
117 TerminalBuffer::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 
127 void
128 TerminalBuffer::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 
138 void
139 TerminalBuffer::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 
149 void
150 TerminalBuffer::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 
160 void
161 TerminalBuffer::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 
171 void
172 TerminalBuffer::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 
182 void
183 TerminalBuffer::SetEncoding(int encoding)
184 {
185 	fEncoding = encoding;
186 }
187 
188 
189 void
190 TerminalBuffer::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 
200 void
201 TerminalBuffer::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 
224 void
225 TerminalBuffer::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 
242 void
243 TerminalBuffer::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 
253 void
254 TerminalBuffer::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 
265 void
266 TerminalBuffer::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 
276 void
277 TerminalBuffer::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 
287 void
288 TerminalBuffer::SetPaletteColor(uint8 index, rgb_color color)
289 {
290 	fColorsPalette[index] = color;
291 }
292 
293 
294 void
295 TerminalBuffer::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 
305 void
306 TerminalBuffer::NotifyListener()
307 {
308 	if (fListenerValid)
309 		fListener.SendMessage(MSG_TERMINAL_BUFFER_CHANGED);
310 }
311 
312 
313 status_t
314 TerminalBuffer::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 
326 status_t
327 TerminalBuffer::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 
377 void
378 TerminalBuffer::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 
392 void
393 TerminalBuffer::UseNormalScreenBuffer()
394 {
395 	if (!fAlternateScreenActive)
396 		return;
397 
398 	_SwitchScreenBuffer();
399 	_InvalidateAll();
400 }
401 
402 
403 void
404 TerminalBuffer::_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