1/*
2 * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6#include "TerminalBuffer.h"
7
8#include <algorithm>
9
10#include <Message.h>
11
12#include "TermConst.h"
13
14
15// #pragma mark - public methods
16
17
18TerminalBuffer::TerminalBuffer()
19	:
20	BLocker("terminal buffer"),
21	fEncoding(M_UTF8),
22	fAlternateScreen(NULL),
23	fAlternateHistory(NULL),
24	fAlternateScreenOffset(0),
25	fListenerValid(false)
26{
27}
28
29
30TerminalBuffer::~TerminalBuffer()
31{
32	delete fAlternateScreen;
33	delete fAlternateHistory;
34}
35
36
37status_t
38TerminalBuffer::Init(int32 width, int32 height, int32 historySize)
39{
40	if (Sem() < 0)
41		return Sem();
42
43	fAlternateScreen = _AllocateLines(width, height);
44	if (fAlternateScreen == NULL)
45		return B_NO_MEMORY;
46
47	for (int32 i = 0; i < height; i++)
48		fAlternateScreen[i]->Clear();
49
50	return BasicTerminalBuffer::Init(width, height, historySize);
51}
52
53
54void
55TerminalBuffer::SetListener(BMessenger listener)
56{
57	fListener = listener;
58	fListenerValid = true;
59}
60
61
62void
63TerminalBuffer::UnsetListener()
64{
65	fListenerValid = false;
66}
67
68
69int
70TerminalBuffer::Encoding() const
71{
72	return fEncoding;
73}
74
75
76void
77TerminalBuffer::ReportX10MouseEvent(bool reportX10MouseEvent)
78{
79	if (fListenerValid) {
80		BMessage message(MSG_REPORT_MOUSE_EVENT);
81		message.AddBool("reportX10MouseEvent", reportX10MouseEvent);
82		fListener.SendMessage(&message);
83	}
84}
85
86
87void
88TerminalBuffer::ReportNormalMouseEvent(bool reportNormalMouseEvent)
89{
90	if (fListenerValid) {
91		BMessage message(MSG_REPORT_MOUSE_EVENT);
92		message.AddBool("reportNormalMouseEvent", reportNormalMouseEvent);
93		fListener.SendMessage(&message);
94	}
95}
96
97
98void
99TerminalBuffer::ReportButtonMouseEvent(bool report)
100{
101	if (fListenerValid) {
102		BMessage message(MSG_REPORT_MOUSE_EVENT);
103		message.AddBool("reportButtonMouseEvent", report);
104		fListener.SendMessage(&message);
105	}
106}
107
108
109void
110TerminalBuffer::ReportAnyMouseEvent(bool reportAnyMouseEvent)
111{
112	if (fListenerValid) {
113		BMessage message(MSG_REPORT_MOUSE_EVENT);
114		message.AddBool("reportAnyMouseEvent", reportAnyMouseEvent);
115		fListener.SendMessage(&message);
116	}
117}
118
119
120void
121TerminalBuffer::SetEncoding(int encoding)
122{
123	fEncoding = encoding;
124}
125
126
127void
128TerminalBuffer::SetTitle(const char* title)
129{
130	if (fListenerValid) {
131		BMessage message(MSG_SET_TERMNAL_TITLE);
132		message.AddString("title", title);
133		fListener.SendMessage(&message);
134	}
135}
136
137
138void
139TerminalBuffer::NotifyQuit(int32 reason)
140{
141	if (fListenerValid) {
142		BMessage message(MSG_QUIT_TERMNAL);
143		message.AddInt32("reason", reason);
144		fListener.SendMessage(&message);
145	}
146}
147
148
149void
150TerminalBuffer::NotifyListener()
151{
152	if (fListenerValid)
153		fListener.SendMessage(MSG_TERMINAL_BUFFER_CHANGED);
154}
155
156
157status_t
158TerminalBuffer::ResizeTo(int32 width, int32 height)
159{
160	int32 historyCapacity = 0;
161	if (!fAlternateScreenActive)
162		historyCapacity = HistoryCapacity();
163	else if (fAlternateHistory != NULL)
164		historyCapacity = fAlternateHistory->Capacity();
165
166	return ResizeTo(width, height, historyCapacity);
167}
168
169
170status_t
171TerminalBuffer::ResizeTo(int32 width, int32 height, int32 historyCapacity)
172{
173	// switch to the normal screen buffer first
174	bool alternateScreenActive = fAlternateScreenActive;
175	if (alternateScreenActive)
176		_SwitchScreenBuffer();
177
178	int32 oldWidth = fWidth;
179	int32 oldHeight = fHeight;
180
181	// Resize the normal screen buffer/history.
182	status_t error = BasicTerminalBuffer::ResizeTo(width, height,
183		historyCapacity);
184	if (error != B_OK) {
185		if (alternateScreenActive)
186			_SwitchScreenBuffer();
187		return error;
188	}
189
190	// Switch to the alternate screen buffer and resize it.
191	if (fAlternateScreen != NULL) {
192		TermPos cursor = fCursor;
193		fCursor.SetTo(0, 0);
194		fWidth = oldWidth;
195		fHeight = oldHeight;
196
197		_SwitchScreenBuffer();
198
199		error = BasicTerminalBuffer::ResizeTo(width, height, 0);
200
201		fWidth = width;
202		fHeight = height;
203		fCursor = cursor;
204
205		// Switch back.
206		if (!alternateScreenActive)
207			_SwitchScreenBuffer();
208
209		if (error != B_OK) {
210			// This sucks -- we can't do anything about it. Delete the
211			// alternate screen buffer.
212			_FreeLines(fAlternateScreen, oldHeight);
213			fAlternateScreen = NULL;
214		}
215	}
216
217	return error;
218}
219
220
221void
222TerminalBuffer::UseAlternateScreenBuffer(bool clear)
223{
224	if (fAlternateScreenActive || fAlternateScreen == NULL)
225		return;
226
227	_SwitchScreenBuffer();
228
229	if (clear)
230		Clear(false);
231
232	_InvalidateAll();
233}
234
235
236void
237TerminalBuffer::UseNormalScreenBuffer()
238{
239	if (!fAlternateScreenActive)
240		return;
241
242	_SwitchScreenBuffer();
243	_InvalidateAll();
244}
245
246
247void
248TerminalBuffer::_SwitchScreenBuffer()
249{
250	std::swap(fScreen, fAlternateScreen);
251	std::swap(fHistory, fAlternateHistory);
252	std::swap(fScreenOffset, fAlternateScreenOffset);
253	fAlternateScreenActive = !fAlternateScreenActive;
254}
255