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