1 /* 2 * Copyright 2013-2023, 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::EnableBracketedPasteMode(bool enable) 118 { 119 if (fListenerValid) { 120 BMessage message(MSG_ENABLE_BRACKETED_PASTE); 121 message.AddBool("enableBracketedPaste", enable); 122 fListener.SendMessage(&message); 123 } 124 } 125 126 127 void 128 TerminalBuffer::ReportX10MouseEvent(bool reportX10MouseEvent) 129 { 130 if (fListenerValid) { 131 BMessage message(MSG_REPORT_MOUSE_EVENT); 132 message.AddBool("reportX10MouseEvent", reportX10MouseEvent); 133 fListener.SendMessage(&message); 134 } 135 } 136 137 138 void 139 TerminalBuffer::ReportNormalMouseEvent(bool reportNormalMouseEvent) 140 { 141 if (fListenerValid) { 142 BMessage message(MSG_REPORT_MOUSE_EVENT); 143 message.AddBool("reportNormalMouseEvent", reportNormalMouseEvent); 144 fListener.SendMessage(&message); 145 } 146 } 147 148 149 void 150 TerminalBuffer::ReportButtonMouseEvent(bool report) 151 { 152 if (fListenerValid) { 153 BMessage message(MSG_REPORT_MOUSE_EVENT); 154 message.AddBool("reportButtonMouseEvent", report); 155 fListener.SendMessage(&message); 156 } 157 } 158 159 160 void 161 TerminalBuffer::ReportAnyMouseEvent(bool reportAnyMouseEvent) 162 { 163 if (fListenerValid) { 164 BMessage message(MSG_REPORT_MOUSE_EVENT); 165 message.AddBool("reportAnyMouseEvent", reportAnyMouseEvent); 166 fListener.SendMessage(&message); 167 } 168 } 169 170 171 void 172 TerminalBuffer::EnableExtendedMouseCoordinates(bool enable) 173 { 174 if (fListenerValid) { 175 BMessage message(MSG_REPORT_MOUSE_EVENT); 176 message.AddBool("enableExtendedMouseCoordinates", enable); 177 fListener.SendMessage(&message); 178 } 179 } 180 181 182 void 183 TerminalBuffer::SetEncoding(int encoding) 184 { 185 fEncoding = encoding; 186 } 187 188 189 void 190 TerminalBuffer::SetTitle(const char* title) 191 { 192 if (fListenerValid) { 193 BMessage message(MSG_SET_TERMINAL_TITLE); 194 message.AddString("title", title); 195 fListener.SendMessage(&message); 196 } 197 } 198 199 200 void 201 TerminalBuffer::SetColors(uint8* indexes, rgb_color* colors, 202 int32 count, bool dynamic) 203 { 204 if (fListenerValid) { 205 BMessage message(MSG_SET_TERMINAL_COLORS); 206 message.AddInt32("count", count); 207 message.AddBool("dynamic", dynamic); 208 message.AddData("index", B_UINT8_TYPE, 209 indexes, sizeof(uint8), true, count); 210 message.AddData("color", B_RGB_COLOR_TYPE, 211 colors, sizeof(rgb_color), true, count); 212 213 for (int i = 1; i < count; i++) { 214 message.AddData("index", B_UINT8_TYPE, &indexes[i], sizeof(uint8)); 215 message.AddData("color", B_RGB_COLOR_TYPE, &colors[i], 216 sizeof(rgb_color)); 217 } 218 219 fListener.SendMessage(&message); 220 } 221 } 222 223 224 void 225 TerminalBuffer::ResetColors(uint8* indexes, int32 count, bool dynamic) 226 { 227 if (fListenerValid) { 228 BMessage message(MSG_RESET_TERMINAL_COLORS); 229 message.AddInt32("count", count); 230 message.AddBool("dynamic", dynamic); 231 message.AddData("index", B_UINT8_TYPE, 232 indexes, sizeof(uint8), true, count); 233 234 for (int i = 1; i < count; i++) 235 message.AddData("index", B_UINT8_TYPE, &indexes[i], sizeof(uint8)); 236 237 fListener.SendMessage(&message); 238 } 239 } 240 241 242 void 243 TerminalBuffer::GetColor(uint8 index) 244 { 245 if (fListenerValid) { 246 BMessage message(MSG_GET_TERMINAL_COLOR); 247 message.AddUInt8("index", index); 248 fListener.SendMessage(&message); 249 } 250 } 251 252 253 void 254 TerminalBuffer::SetCursorStyle(int32 style, bool blinking) 255 { 256 if (fListenerValid) { 257 BMessage message(MSG_SET_CURSOR_STYLE); 258 message.AddInt32("style", style); 259 message.AddBool("blinking", blinking); 260 fListener.SendMessage(&message); 261 } 262 } 263 264 265 void 266 TerminalBuffer::SetCursorBlinking(bool blinking) 267 { 268 if (fListenerValid) { 269 BMessage message(MSG_SET_CURSOR_STYLE); 270 message.AddBool("blinking", blinking); 271 fListener.SendMessage(&message); 272 } 273 } 274 275 276 void 277 TerminalBuffer::SetCursorHidden(bool hidden) 278 { 279 if (fListenerValid) { 280 BMessage message(MSG_SET_CURSOR_STYLE); 281 message.AddBool("hidden", hidden); 282 fListener.SendMessage(&message); 283 } 284 } 285 286 287 void 288 TerminalBuffer::SetPaletteColor(uint8 index, rgb_color color) 289 { 290 fColorsPalette[index] = color; 291 } 292 293 294 void 295 TerminalBuffer::NotifyQuit(int32 reason) 296 { 297 if (fListenerValid) { 298 BMessage message(MSG_QUIT_TERMNAL); 299 message.AddInt32("reason", reason); 300 fListener.SendMessage(&message); 301 } 302 } 303 304 305 void 306 TerminalBuffer::NotifyListener() 307 { 308 if (fListenerValid) 309 fListener.SendMessage(MSG_TERMINAL_BUFFER_CHANGED); 310 } 311 312 313 status_t 314 TerminalBuffer::ResizeTo(int32 width, int32 height) 315 { 316 int32 historyCapacity = 0; 317 if (!fAlternateScreenActive) 318 historyCapacity = HistoryCapacity(); 319 else if (fAlternateHistory != NULL) 320 historyCapacity = fAlternateHistory->Capacity(); 321 322 return ResizeTo(width, height, historyCapacity); 323 } 324 325 326 status_t 327 TerminalBuffer::ResizeTo(int32 width, int32 height, int32 historyCapacity) 328 { 329 // switch to the normal screen buffer first 330 bool alternateScreenActive = fAlternateScreenActive; 331 if (alternateScreenActive) 332 _SwitchScreenBuffer(); 333 334 int32 oldWidth = fWidth; 335 int32 oldHeight = fHeight; 336 337 // Resize the normal screen buffer/history. 338 status_t error = BasicTerminalBuffer::ResizeTo(width, height, 339 historyCapacity); 340 if (error != B_OK) { 341 if (alternateScreenActive) 342 _SwitchScreenBuffer(); 343 return error; 344 } 345 346 // Switch to the alternate screen buffer and resize it. 347 if (fAlternateScreen != NULL) { 348 TermPos cursor = fCursor; 349 fCursor.SetTo(0, 0); 350 fWidth = oldWidth; 351 fHeight = oldHeight; 352 353 _SwitchScreenBuffer(); 354 355 error = BasicTerminalBuffer::ResizeTo(width, height, 0); 356 357 fWidth = width; 358 fHeight = height; 359 fCursor = cursor; 360 361 // Switch back. 362 if (!alternateScreenActive) 363 _SwitchScreenBuffer(); 364 365 if (error != B_OK) { 366 // This sucks -- we can't do anything about it. Delete the 367 // alternate screen buffer. 368 _FreeLines(fAlternateScreen, oldHeight); 369 fAlternateScreen = NULL; 370 } 371 } 372 373 return error; 374 } 375 376 377 void 378 TerminalBuffer::UseAlternateScreenBuffer(bool clear) 379 { 380 if (fAlternateScreenActive || fAlternateScreen == NULL) 381 return; 382 383 _SwitchScreenBuffer(); 384 385 if (clear) 386 Clear(false); 387 388 _InvalidateAll(); 389 } 390 391 392 void 393 TerminalBuffer::UseNormalScreenBuffer() 394 { 395 if (!fAlternateScreenActive) 396 return; 397 398 _SwitchScreenBuffer(); 399 _InvalidateAll(); 400 } 401 402 403 void 404 TerminalBuffer::_SwitchScreenBuffer() 405 { 406 std::swap(fScreen, fAlternateScreen); 407 std::swap(fHistory, fAlternateHistory); 408 std::swap(fScreenOffset, fAlternateScreenOffset); 409 std::swap(fAttributes, fAlternateAttributes); 410 fAlternateScreenActive = !fAlternateScreenActive; 411 } 412