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::SetCursorStyle(int32 style, bool blinking) 233 { 234 if (fListenerValid) { 235 BMessage message(MSG_SET_CURSOR_STYLE); 236 message.AddInt32("style", style); 237 message.AddBool("blinking", blinking); 238 fListener.SendMessage(&message); 239 } 240 } 241 242 243 void 244 TerminalBuffer::SetCursorBlinking(bool blinking) 245 { 246 if (fListenerValid) { 247 BMessage message(MSG_SET_CURSOR_STYLE); 248 message.AddBool("blinking", blinking); 249 fListener.SendMessage(&message); 250 } 251 } 252 253 254 void 255 TerminalBuffer::SetCursorHidden(bool hidden) 256 { 257 if (fListenerValid) { 258 BMessage message(MSG_SET_CURSOR_STYLE); 259 message.AddBool("hidden", hidden); 260 fListener.SendMessage(&message); 261 } 262 } 263 264 265 void 266 TerminalBuffer::SetPaletteColor(uint8 index, rgb_color color) 267 { 268 fColorsPalette[index] = color; 269 } 270 271 272 rgb_color 273 TerminalBuffer::PaletteColor(uint8 index) 274 { 275 return fColorsPalette[index]; 276 } 277 278 279 int 280 TerminalBuffer::GuessPaletteColor(int red, int green, int blue) 281 { 282 int distance = 255 * 100; 283 int index = -1; 284 for (uint32 i = 0; i < kTermColorCount && distance > 0; i++) { 285 rgb_color color = fColorsPalette[i]; 286 int r = 30 * abs(color.red - red); 287 int g = 59 * abs(color.green - green); 288 int b = 11 * abs(color.blue - blue); 289 int d = r + g + b; 290 if (distance > d) { 291 index = i; 292 distance = d; 293 } 294 } 295 296 return min_c(index, int(kTermColorCount - 1)); 297 } 298 299 300 void 301 TerminalBuffer::NotifyQuit(int32 reason) 302 { 303 if (fListenerValid) { 304 BMessage message(MSG_QUIT_TERMNAL); 305 message.AddInt32("reason", reason); 306 fListener.SendMessage(&message); 307 } 308 } 309 310 311 void 312 TerminalBuffer::NotifyListener() 313 { 314 if (fListenerValid) 315 fListener.SendMessage(MSG_TERMINAL_BUFFER_CHANGED); 316 } 317 318 319 status_t 320 TerminalBuffer::ResizeTo(int32 width, int32 height) 321 { 322 int32 historyCapacity = 0; 323 if (!fAlternateScreenActive) 324 historyCapacity = HistoryCapacity(); 325 else if (fAlternateHistory != NULL) 326 historyCapacity = fAlternateHistory->Capacity(); 327 328 return ResizeTo(width, height, historyCapacity); 329 } 330 331 332 status_t 333 TerminalBuffer::ResizeTo(int32 width, int32 height, int32 historyCapacity) 334 { 335 // switch to the normal screen buffer first 336 bool alternateScreenActive = fAlternateScreenActive; 337 if (alternateScreenActive) 338 _SwitchScreenBuffer(); 339 340 int32 oldWidth = fWidth; 341 int32 oldHeight = fHeight; 342 343 // Resize the normal screen buffer/history. 344 status_t error = BasicTerminalBuffer::ResizeTo(width, height, 345 historyCapacity); 346 if (error != B_OK) { 347 if (alternateScreenActive) 348 _SwitchScreenBuffer(); 349 return error; 350 } 351 352 // Switch to the alternate screen buffer and resize it. 353 if (fAlternateScreen != NULL) { 354 TermPos cursor = fCursor; 355 fCursor.SetTo(0, 0); 356 fWidth = oldWidth; 357 fHeight = oldHeight; 358 359 _SwitchScreenBuffer(); 360 361 error = BasicTerminalBuffer::ResizeTo(width, height, 0); 362 363 fWidth = width; 364 fHeight = height; 365 fCursor = cursor; 366 367 // Switch back. 368 if (!alternateScreenActive) 369 _SwitchScreenBuffer(); 370 371 if (error != B_OK) { 372 // This sucks -- we can't do anything about it. Delete the 373 // alternate screen buffer. 374 _FreeLines(fAlternateScreen, oldHeight); 375 fAlternateScreen = NULL; 376 } 377 } 378 379 return error; 380 } 381 382 383 void 384 TerminalBuffer::UseAlternateScreenBuffer(bool clear) 385 { 386 if (fAlternateScreenActive || fAlternateScreen == NULL) 387 return; 388 389 _SwitchScreenBuffer(); 390 391 if (clear) 392 Clear(false); 393 394 _InvalidateAll(); 395 } 396 397 398 void 399 TerminalBuffer::UseNormalScreenBuffer() 400 { 401 if (!fAlternateScreenActive) 402 return; 403 404 _SwitchScreenBuffer(); 405 _InvalidateAll(); 406 } 407 408 409 void 410 TerminalBuffer::_SwitchScreenBuffer() 411 { 412 std::swap(fScreen, fAlternateScreen); 413 std::swap(fHistory, fAlternateHistory); 414 std::swap(fScreenOffset, fAlternateScreenOffset); 415 std::swap(fAttributes, fAlternateAttributes); 416 fAlternateScreenActive = !fAlternateScreenActive; 417 } 418