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