xref: /haiku/src/apps/terminal/TerminalBuffer.cpp (revision 6f80a9801fedbe7355c4360bd204ba746ec3ec2d)
1 /*
2  * Copyright 2013, 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::ReportX10MouseEvent(bool reportX10MouseEvent)
118 {
119 	if (fListenerValid) {
120 		BMessage message(MSG_REPORT_MOUSE_EVENT);
121 		message.AddBool("reportX10MouseEvent", reportX10MouseEvent);
122 		fListener.SendMessage(&message);
123 	}
124 }
125 
126 
127 void
128 TerminalBuffer::ReportNormalMouseEvent(bool reportNormalMouseEvent)
129 {
130 	if (fListenerValid) {
131 		BMessage message(MSG_REPORT_MOUSE_EVENT);
132 		message.AddBool("reportNormalMouseEvent", reportNormalMouseEvent);
133 		fListener.SendMessage(&message);
134 	}
135 }
136 
137 
138 void
139 TerminalBuffer::ReportButtonMouseEvent(bool report)
140 {
141 	if (fListenerValid) {
142 		BMessage message(MSG_REPORT_MOUSE_EVENT);
143 		message.AddBool("reportButtonMouseEvent", report);
144 		fListener.SendMessage(&message);
145 	}
146 }
147 
148 
149 void
150 TerminalBuffer::ReportAnyMouseEvent(bool reportAnyMouseEvent)
151 {
152 	if (fListenerValid) {
153 		BMessage message(MSG_REPORT_MOUSE_EVENT);
154 		message.AddBool("reportAnyMouseEvent", reportAnyMouseEvent);
155 		fListener.SendMessage(&message);
156 	}
157 }
158 
159 
160 void
161 TerminalBuffer::EnableExtendedMouseCoordinates(bool enable)
162 {
163 	if (fListenerValid) {
164 		BMessage message(MSG_REPORT_MOUSE_EVENT);
165 		message.AddBool("enableExtendedMouseCoordinates", enable);
166 		fListener.SendMessage(&message);
167 	}
168 }
169 
170 
171 void
172 TerminalBuffer::SetEncoding(int encoding)
173 {
174 	fEncoding = encoding;
175 }
176 
177 
178 void
179 TerminalBuffer::SetTitle(const char* title)
180 {
181 	if (fListenerValid) {
182 		BMessage message(MSG_SET_TERMINAL_TITLE);
183 		message.AddString("title", title);
184 		fListener.SendMessage(&message);
185 	}
186 }
187 
188 
189 void
190 TerminalBuffer::SetColors(uint8* indexes, rgb_color* colors,
191 		int32 count, bool dynamic)
192 {
193 	if (fListenerValid) {
194 		BMessage message(MSG_SET_TERMINAL_COLORS);
195 		message.AddInt32("count", count);
196 		message.AddBool("dynamic", dynamic);
197 		message.AddData("index", B_UINT8_TYPE,
198 					indexes, sizeof(uint8), true, count);
199 		message.AddData("color", B_RGB_COLOR_TYPE,
200 					colors, sizeof(rgb_color), true, count);
201 
202 		for (int i = 1; i < count; i++) {
203 			message.AddData("index", B_UINT8_TYPE, &indexes[i], sizeof(uint8));
204 			message.AddData("color", B_RGB_COLOR_TYPE, &colors[i],
205 					sizeof(rgb_color));
206 		}
207 
208 		fListener.SendMessage(&message);
209 	}
210 }
211 
212 
213 void
214 TerminalBuffer::ResetColors(uint8* indexes, int32 count, bool dynamic)
215 {
216 	if (fListenerValid) {
217 		BMessage message(MSG_RESET_TERMINAL_COLORS);
218 		message.AddInt32("count", count);
219 		message.AddBool("dynamic", dynamic);
220 		message.AddData("index", B_UINT8_TYPE,
221 					indexes, sizeof(uint8), true, count);
222 
223 		for (int i = 1; i < count; i++)
224 			message.AddData("index", B_UINT8_TYPE, &indexes[i], sizeof(uint8));
225 
226 		fListener.SendMessage(&message);
227 	}
228 }
229 
230 
231 void
232 TerminalBuffer::GetColor(uint8 index)
233 {
234 	if (fListenerValid) {
235 		BMessage message(MSG_GET_TERMINAL_COLOR);
236 		message.AddUInt8("index", index);
237 		fListener.SendMessage(&message);
238 	}
239 }
240 
241 
242 void
243 TerminalBuffer::SetCursorStyle(int32 style, bool blinking)
244 {
245 	if (fListenerValid) {
246 		BMessage message(MSG_SET_CURSOR_STYLE);
247 		message.AddInt32("style", style);
248 		message.AddBool("blinking", blinking);
249 		fListener.SendMessage(&message);
250 	}
251 }
252 
253 
254 void
255 TerminalBuffer::SetCursorBlinking(bool blinking)
256 {
257 	if (fListenerValid) {
258 		BMessage message(MSG_SET_CURSOR_STYLE);
259 		message.AddBool("blinking", blinking);
260 		fListener.SendMessage(&message);
261 	}
262 }
263 
264 
265 void
266 TerminalBuffer::SetCursorHidden(bool hidden)
267 {
268 	if (fListenerValid) {
269 		BMessage message(MSG_SET_CURSOR_STYLE);
270 		message.AddBool("hidden", hidden);
271 		fListener.SendMessage(&message);
272 	}
273 }
274 
275 
276 void
277 TerminalBuffer::SetPaletteColor(uint8 index, rgb_color color)
278 {
279 	fColorsPalette[index] = color;
280 }
281 
282 
283 void
284 TerminalBuffer::NotifyQuit(int32 reason)
285 {
286 	if (fListenerValid) {
287 		BMessage message(MSG_QUIT_TERMNAL);
288 		message.AddInt32("reason", reason);
289 		fListener.SendMessage(&message);
290 	}
291 }
292 
293 
294 void
295 TerminalBuffer::NotifyListener()
296 {
297 	if (fListenerValid)
298 		fListener.SendMessage(MSG_TERMINAL_BUFFER_CHANGED);
299 }
300 
301 
302 status_t
303 TerminalBuffer::ResizeTo(int32 width, int32 height)
304 {
305 	int32 historyCapacity = 0;
306 	if (!fAlternateScreenActive)
307 		historyCapacity = HistoryCapacity();
308 	else if (fAlternateHistory != NULL)
309 		historyCapacity = fAlternateHistory->Capacity();
310 
311 	return ResizeTo(width, height, historyCapacity);
312 }
313 
314 
315 status_t
316 TerminalBuffer::ResizeTo(int32 width, int32 height, int32 historyCapacity)
317 {
318 	// switch to the normal screen buffer first
319 	bool alternateScreenActive = fAlternateScreenActive;
320 	if (alternateScreenActive)
321 		_SwitchScreenBuffer();
322 
323 	int32 oldWidth = fWidth;
324 	int32 oldHeight = fHeight;
325 
326 	// Resize the normal screen buffer/history.
327 	status_t error = BasicTerminalBuffer::ResizeTo(width, height,
328 		historyCapacity);
329 	if (error != B_OK) {
330 		if (alternateScreenActive)
331 			_SwitchScreenBuffer();
332 		return error;
333 	}
334 
335 	// Switch to the alternate screen buffer and resize it.
336 	if (fAlternateScreen != NULL) {
337 		TermPos cursor = fCursor;
338 		fCursor.SetTo(0, 0);
339 		fWidth = oldWidth;
340 		fHeight = oldHeight;
341 
342 		_SwitchScreenBuffer();
343 
344 		error = BasicTerminalBuffer::ResizeTo(width, height, 0);
345 
346 		fWidth = width;
347 		fHeight = height;
348 		fCursor = cursor;
349 
350 		// Switch back.
351 		if (!alternateScreenActive)
352 			_SwitchScreenBuffer();
353 
354 		if (error != B_OK) {
355 			// This sucks -- we can't do anything about it. Delete the
356 			// alternate screen buffer.
357 			_FreeLines(fAlternateScreen, oldHeight);
358 			fAlternateScreen = NULL;
359 		}
360 	}
361 
362 	return error;
363 }
364 
365 
366 void
367 TerminalBuffer::UseAlternateScreenBuffer(bool clear)
368 {
369 	if (fAlternateScreenActive || fAlternateScreen == NULL)
370 		return;
371 
372 	_SwitchScreenBuffer();
373 
374 	if (clear)
375 		Clear(false);
376 
377 	_InvalidateAll();
378 }
379 
380 
381 void
382 TerminalBuffer::UseNormalScreenBuffer()
383 {
384 	if (!fAlternateScreenActive)
385 		return;
386 
387 	_SwitchScreenBuffer();
388 	_InvalidateAll();
389 }
390 
391 
392 void
393 TerminalBuffer::_SwitchScreenBuffer()
394 {
395 	std::swap(fScreen, fAlternateScreen);
396 	std::swap(fHistory, fAlternateHistory);
397 	std::swap(fScreenOffset, fAlternateScreenOffset);
398 	std::swap(fAttributes, fAlternateAttributes);
399 	fAlternateScreenActive = !fAlternateScreenActive;
400 }
401