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