xref: /haiku/src/apps/terminal/TerminalBuffer.cpp (revision 4c8e85b316c35a9161f5a1c50ad70bc91c83a76f)
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(0),
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 rgb_color
284 TerminalBuffer::PaletteColor(uint8 index)
285 {
286 	return fColorsPalette[index];
287 }
288 
289 
290 int
291 TerminalBuffer::GuessPaletteColor(int red, int green, int blue)
292 {
293 	int distance = 255 * 100;
294 	int index = -1;
295 	for (uint32 i = 0; i < kTermColorCount && distance > 0; i++) {
296 		rgb_color color = fColorsPalette[i];
297 		int r = 30 * abs(color.red - red);
298 		int g = 59 * abs(color.green - green);
299 		int b = 11 * abs(color.blue - blue);
300 		int d = r + g + b;
301 		if (distance > d) {
302 			index = i;
303 			distance = d;
304 		}
305 	}
306 
307 	return min_c(index, int(kTermColorCount - 1));
308 }
309 
310 
311 void
312 TerminalBuffer::NotifyQuit(int32 reason)
313 {
314 	if (fListenerValid) {
315 		BMessage message(MSG_QUIT_TERMNAL);
316 		message.AddInt32("reason", reason);
317 		fListener.SendMessage(&message);
318 	}
319 }
320 
321 
322 void
323 TerminalBuffer::NotifyListener()
324 {
325 	if (fListenerValid)
326 		fListener.SendMessage(MSG_TERMINAL_BUFFER_CHANGED);
327 }
328 
329 
330 status_t
331 TerminalBuffer::ResizeTo(int32 width, int32 height)
332 {
333 	int32 historyCapacity = 0;
334 	if (!fAlternateScreenActive)
335 		historyCapacity = HistoryCapacity();
336 	else if (fAlternateHistory != NULL)
337 		historyCapacity = fAlternateHistory->Capacity();
338 
339 	return ResizeTo(width, height, historyCapacity);
340 }
341 
342 
343 status_t
344 TerminalBuffer::ResizeTo(int32 width, int32 height, int32 historyCapacity)
345 {
346 	// switch to the normal screen buffer first
347 	bool alternateScreenActive = fAlternateScreenActive;
348 	if (alternateScreenActive)
349 		_SwitchScreenBuffer();
350 
351 	int32 oldWidth = fWidth;
352 	int32 oldHeight = fHeight;
353 
354 	// Resize the normal screen buffer/history.
355 	status_t error = BasicTerminalBuffer::ResizeTo(width, height,
356 		historyCapacity);
357 	if (error != B_OK) {
358 		if (alternateScreenActive)
359 			_SwitchScreenBuffer();
360 		return error;
361 	}
362 
363 	// Switch to the alternate screen buffer and resize it.
364 	if (fAlternateScreen != NULL) {
365 		TermPos cursor = fCursor;
366 		fCursor.SetTo(0, 0);
367 		fWidth = oldWidth;
368 		fHeight = oldHeight;
369 
370 		_SwitchScreenBuffer();
371 
372 		error = BasicTerminalBuffer::ResizeTo(width, height, 0);
373 
374 		fWidth = width;
375 		fHeight = height;
376 		fCursor = cursor;
377 
378 		// Switch back.
379 		if (!alternateScreenActive)
380 			_SwitchScreenBuffer();
381 
382 		if (error != B_OK) {
383 			// This sucks -- we can't do anything about it. Delete the
384 			// alternate screen buffer.
385 			_FreeLines(fAlternateScreen, oldHeight);
386 			fAlternateScreen = NULL;
387 		}
388 	}
389 
390 	return error;
391 }
392 
393 
394 void
395 TerminalBuffer::UseAlternateScreenBuffer(bool clear)
396 {
397 	if (fAlternateScreenActive || fAlternateScreen == NULL)
398 		return;
399 
400 	_SwitchScreenBuffer();
401 
402 	if (clear)
403 		Clear(false);
404 
405 	_InvalidateAll();
406 }
407 
408 
409 void
410 TerminalBuffer::UseNormalScreenBuffer()
411 {
412 	if (!fAlternateScreenActive)
413 		return;
414 
415 	_SwitchScreenBuffer();
416 	_InvalidateAll();
417 }
418 
419 
420 void
421 TerminalBuffer::_SwitchScreenBuffer()
422 {
423 	std::swap(fScreen, fAlternateScreen);
424 	std::swap(fHistory, fAlternateHistory);
425 	std::swap(fScreenOffset, fAlternateScreenOffset);
426 	std::swap(fAttributes, fAlternateAttributes);
427 	fAlternateScreenActive = !fAlternateScreenActive;
428 }
429