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